{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Decorators\n", "\n", "![UFO](https://i.ytimg.com/vi/zUFtNLeUXgU/maxresdefault.jpg)\n", "\n", "\n", "I use UFO as a decorator not because I want or need people to believe in UFOs, but because the science fiction idea of being abducted is you stay the same but for something lasting the UFO did to you.\n", "\n", "In the case of decorator syntax that's useful because to \"decorate\" (\"abduct\") is to \n", "\n", " * feed a function to a callable (the decorator), and \n", " * keep that function's name for whatever gets returned\n", " \n", "Since function type objects are just objects with a ```__dict__```, we're free to apply arbitrary attributes to them. Lets have the UFO decorator decorate any function with a new attribute named 'abducted'." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def UFO(f):\n", " setattr(f, 'abducted', True) # f.abducted = True same thing\n", " return f\n", "\n", "@UFO\n", "def addS(s):\n", " return s + \"S\"\n", "\n", "@UFO\n", "def addX(s):\n", " return s + \"X\"" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hasattr(addX, 'abducted')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The value of abducted for addS is: True\n" ] } ], "source": [ "if hasattr(addS, 'abducted'):\n", " print(\"The value of abducted for addS is:\", addS.abducted)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the example below, the Composer class \"decorates\" the two following functions, meaning the Composer instances become the new proxies for the functions they swallowed. The original functions are still on tap, through ```__call__```. \n", "\n", "Furthermore, when two such Composer types are multiplied, their internal functions get composed together, into a new internalized function." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "class Composer:\n", " \n", " def __init__(self, f):\n", " self.func = f\n", " \n", " def __call__(self, s):\n", " return self.func(s)\n", " \n", " def __mul__(self, other):\n", " def new(s):\n", " return self(other(s))\n", " return Composer(new)\n", "\n", "@Composer\n", "def F(x):\n", " return x * x\n", "\n", "@Composer\n", "def G(x):\n", " return x + 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below is simple composition of functions. This is valid Python even if the Composer decorator is left out, i.e. function type objects would normally have no problem composing with one another in this way. \n", "\n", "To compose F and G means going F(G(x)) for some x." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "184884260614963204" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F(G(F(F(F(G(10))))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thanks to Compose, the \"class decorator\" (a decorator that happens to be a class), our F and G are actually Compose type objects, so have this additional ability to compose into other Compose type objects. We don't need an argument until we call the final H." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "184884260614963204" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "H = F*G*F*F*F*G # the functions themselves may be multiplied\n", "H(10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }