{"id":5528,"date":"2022-04-01T23:00:40","date_gmt":"2022-04-01T23:00:40","guid":{"rendered":"https:\/\/www.aiproblog.com\/index.php\/2022\/04\/01\/a-gentle-introduction-to-decorators-in-python\/"},"modified":"2022-04-01T23:00:40","modified_gmt":"2022-04-01T23:00:40","slug":"a-gentle-introduction-to-decorators-in-python","status":"publish","type":"post","link":"https:\/\/www.aiproblog.com\/index.php\/2022\/04\/01\/a-gentle-introduction-to-decorators-in-python\/","title":{"rendered":"A Gentle Introduction to Decorators in Python"},"content":{"rendered":"<p>Author: Zhe Ming Chng<\/p>\n<div>\n<p>When working on code, whether we know it or not, we often come across the decorator design pattern. This is a programming technique to extend the functionality of classes or functions without modifying them. The decorator design pattern allows us to mix and match extensions easily. Python has a decorator syntax rooted in the decorator design pattern. Knowing how to make and use a decorator can help you write more powerful code.<\/p>\n<p>In this post, you will discover the decorator pattern and Python\u2019s function decorators.<\/p>\n<p>After completing this tutorial, you will learn:<\/p>\n<ul>\n<li>What is the decorator pattern, and why is it useful<\/li>\n<li>Python\u2019s function decorators and how to use them<\/li>\n<\/ul>\n<p>Let\u2019s get started!<\/p>\n<div id=\"attachment_13380\" style=\"width: 810px\" class=\"wp-caption aligncenter\">\n<img decoding=\"async\" aria-describedby=\"caption-attachment-13380\" class=\"size-full wp-image-13380\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-scaled.jpg\" alt=\"\" width=\"800\" srcset=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-scaled.jpg 2560w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-300x200.jpg 300w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-1024x684.jpg 1024w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-768x513.jpg 768w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-1536x1025.jpg 1536w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-2048x1367.jpg 2048w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/pexels-olya-kobruseva-6560995-600x400.jpg 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\"><\/p>\n<p id=\"caption-attachment-13380\" class=\"wp-caption-text\">A Gentle Introduction to Decorators in Python<br \/>Photo by <a href=\"https:\/\/www.pexels.com\/photo\/a-book-beside-a-cup-of-coffee-6560995\/\">Olya Kobruseva<\/a>. Some rights reserved.<\/p>\n<\/div>\n<h2>Overview<\/h2>\n<p>This tutorial is divided into four parts:<\/p>\n<ul>\n<li>What is the decorator pattern, and why is it useful?<\/li>\n<li>Function decorators in Python<\/li>\n<li>The use cases of decorators<\/li>\n<li>Some practical examples of decorators<\/li>\n<\/ul>\n<h2>What is the decorator pattern, and why is it useful?<\/h2>\n<p>The decorator pattern is a software design pattern that allows us to dynamically add functionality to classes without creating subclasses and affecting the behavior of other objects of the same class. By using the decorator pattern, we can easily generate different permutations of functionality that we might want without creating an exponentially increasing number of subclasses, making our code increasingly complex and bloated.<\/p>\n<p>Decorators are usually implemented as sub-interfaces of the main interface that we want to implement and store an object of the main interface\u2019s type. It will then modify the methods to which it wants to add certain functionality by overriding the methods in the original interface and calling on methods from the stored object.<\/p>\n<div id=\"attachment_13346\" style=\"width: 510px\" class=\"wp-caption aligncenter\">\n<a href=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/Decorator-UML-Class-Diagram.png\"><img decoding=\"async\" aria-describedby=\"caption-attachment-13346\" class=\"wp-image-13346\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/Decorator-UML-Class-Diagram.png\" alt=\"\" width=\"500\" srcset=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/Decorator-UML-Class-Diagram.png 904w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/Decorator-UML-Class-Diagram-300x263.png 300w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/03\/Decorator-UML-Class-Diagram-768x672.png 768w\" sizes=\"(max-width: 904px) 100vw, 904px\"><\/a><\/p>\n<p id=\"caption-attachment-13346\" class=\"wp-caption-text\">UML class diagram for decorator pattern<\/p>\n<\/div>\n<p>Above is the UML class diagram for the decorator design pattern. The decorator abstract class contains an object of type <code>OriginalInterface<\/code>; this is the object whose functionality the decorator will be modifying. To instantiate our concrete <code>DecoratorClass<\/code>, we would need to pass in a concrete class that implements the <code>OriginalInterface,<\/code> and then when we make method calls to <code>DecoratorClass.method1()<\/code>, our <code>DecoratorClass<\/code> should modify the output from the object\u2019s <code>method1()<\/code>.<\/p>\n<p>With Python, however, we are able to simplify many of these design patterns due to dynamic typing along with functions and classes being first-class objects. While modifying a class or a function without changing the implementation remained the key idea of decorators, we will explore Python\u2019s decorator syntax in the following.<\/p>\n<h2>Function Decorators in Python<\/h2>\n<p>A function decorator is an incredibly useful feature in Python. It is built upon the idea that functions and classes are first-class objects in Python.<\/p>\n<p>Let\u2019s consider a simple example, that is, to call a function twice. Since a Python function is an object and we can pass a function as an argument to another function, this task can be done as follows:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">def repeat(fn):\r\n    fn()\r\n    fn()\r\n\r\ndef hello_world():\r\n    print(\"Hello world!\")\r\n\r\nrepeat(hello_world)<\/pre>\n<p>Again, since a Python function is an object, we can make a function to return another function, which is to execute yet another function twice. This is done as follows:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">def repeat_decorator(fn):\r\n    def decorated_fn():\r\n        fn()\r\n        fn()\r\n    # returns a function\r\n    return decorated_fn\r\n\r\ndef hello_world():\r\n    print (\"Hello world!\")\r\n\r\nhello_world_twice = repeat_decorator(hello_world)\r\n\r\n# call the function\r\nhello_world_twice()<\/pre>\n<p>The function returned by <code>repeat_decorator()<\/code> above is created when it is invoked, as it depends on the argument provided. In the above, we passed the <code>hello_world<\/code> function as an argument to the <code>repeat_decorator()<\/code> function, and it returns the <code>decorated_fn<\/code> function, which is assigned to <code>hello_world_twice<\/code>. Afterward, we can invoke <code>hello_world_twice()<\/code> since it is now a function.<\/p>\n<p>The idea of decorator pattern applies here. But we do not need to define the interface and subclasses explicitly. In fact, <code>hello_world<\/code> is a name defined as a function in the above example. There is nothing preventing us from redefining this name to something else. Hence we can also do the following:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">def repeat_decorator(fn):\r\n    def decorated_fn():\r\n        fn()\r\n        fn()\r\n    # returns a function\r\n    return decorated_fn\r\n\r\ndef hello_world():\r\n    print (\"Hello world!\")\r\n\r\nhello_world = repeat_decorator(hello_world)\r\n\r\n# call the function\r\nhello_world()<\/pre>\n<p>That is, instead of assigning the newly created function to <code>hello_world_twice<\/code>, we overwrite <code>hello_world<\/code> instead. While the name <code>hello_world<\/code> is reassigned to another function, the previous function still exists but is just not exposed to us.<\/p>\n<p>Indeed, the above code is functionally equivalent to the following:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># function decorator that calls the function twice\r\ndef repeat_decorator(fn):\r\n    def decorated_fn():\r\n        fn()\r\n        fn()\r\n    # returns a function\r\n    return decorated_fn\r\n\r\n# using the decorator on hello_world function\r\n@repeat_decorator\r\ndef hello_world():\r\n    print (\"Hello world!\")\r\n\r\n# call the function\r\nhello_world()<\/pre>\n<p>In the above code, <code>@repeat_decorator<\/code> before a function definition means to pass the function into <code>repeat_decorator()<\/code> and reassign its name to the output. That is, to mean <code>hello_world = repeat_decorator(hello_world)<\/code>. The <code>@<\/code> line is the decorator syntax in Python.<\/p>\n<p><strong>Note:<\/strong> <code>@<\/code> syntax is also used in Java but has a different meaning where it\u2019s an annotation that is basically metadata and not a decorator.<\/p>\n<p>We can also implement decorators that take in arguments, but this would be a bit more complicated as we need to have one more layer of nesting. If we extend our example above to define the number of times to repeat the function call:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">def repeat_decorator(num_repeats = 2):\r\n    # repeat_decorator should return a function that's a decorator\r\n    def inner_decorator(fn):\r\n        def decorated_fn():\r\n            for i in range(num_repeats):\r\n                fn()\r\n        # return the new function\r\n        return decorated_fn\r\n    # return the decorator that actually takes the function in as the input\r\n    return inner_decorator\r\n\r\n# use the decorator with num_repeats argument set as 5 to repeat the function call 5 times\r\n@repeat_decorator(5)\r\ndef hello_world():\r\n    print(\"Hello world!\")\r\n\r\n# call the function\r\nhello_world()<\/pre>\n<p>The <code>repeat_decorator()<\/code> takes in an argument and returns a function which is the actual decorator for the <code>hello_world<\/code> function (i.e., invoking <code>repeat_decorator(5)<\/code> returns <code>inner_decorator<\/code> with the local variable <code>num_repeats = 5<\/code> set). The above code will print the following:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">Hello world!\r\nHello world!\r\nHello world!\r\nHello world!\r\nHello world!<\/pre>\n<p>Before we end this section, we should remember that decorators can also be applied to classes in addition to functions. Since class in Python is also an object, we may redefine a class in a similar fashion.<\/p>\n<h2>The Use Cases of Decorators<\/h2>\n<p>The decorator syntax in Python made the use of decorators easier. There are many reasons we may use a decorator. One of the most common use cases is to convert data implicitly. For example, we may define a function that assumes all operations are based on numpy arrays and then make a decorator to ensure that happens by modifying the input:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># function decorator to ensure numpy input\r\ndef ensure_numpy(fn):\r\n    def decorated_function(data):\r\n        # converts input to numpy array\r\n        array = np.asarray(data)\r\n        # calls fn on input numpy array\r\n        return fn(array)\r\n    return decorated_function<\/pre>\n<p>We can further add to our decorator by modifying the output of the function, such as rounding off floating point values:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># function decorator to ensure numpy input\r\n# and round off output to 4 decimal places\r\ndef ensure_numpy(fn):\r\n    def decorated_function(data):\r\n        array = np.asarray(data)\r\n        output = fn(array)\r\n        return np.around(output, 4)\r\n    return decorated_function<\/pre>\n<p>Let\u2019s consider the example of finding the sum of an array. A numpy array has <code>sum()<\/code> built-in, as does pandas DataFrame. But the latter is to sum over columns rather than sum over all elements. Hence a numpy array will sum to one floating point value while a DataFrame will sum to a vector of values. But with the above decorator, we can write a function that gives you consistent output in both cases:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport pandas as pd\r\n\r\n# function decorator to ensure numpy input\r\n# and round off output to 4 decimal places\r\ndef ensure_numpy(fn):\r\n    def decorated_function(data):\r\n        array = np.asarray(data)\r\n        output = fn(array)\r\n        return np.around(output, 4)\r\n    return decorated_function\r\n\r\n@ensure_numpy\r\ndef numpysum(array):\r\n    return array.sum()\r\n\r\nx = np.random.randn(10,3)\r\ny = pd.DataFrame(x, columns=[\"A\", \"B\", \"C\"])\r\n\r\n# output of numpy .sum() function\r\nprint(\"x.sum():\", x.sum())\r\nprint()\r\n\r\n# output of pandas .sum() funuction\r\nprint(\"y.sum():\", y.sum())\r\nprint(y.sum())\r\nprint()\r\n\r\n# calling decorated numpysum function\r\nprint(\"numpysum(x):\", numpysum(x))\r\nprint(\"numpysum(y):\", numpysum(y))<\/pre>\n<p>Running the above code gives us the output:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">x.sum(): 0.3948331694737762\r\n\r\ny.sum(): A   -1.175484\r\nB    2.496056\r\nC   -0.925739\r\ndtype: float64\r\nA   -1.175484\r\nB    2.496056\r\nC   -0.925739\r\ndtype: float64\r\n\r\nnumpysum(x): 0.3948\r\nnumpysum(y): 0.3948<\/pre>\n<p>This is a simple example. But imagine if we define a new function that computes the standard deviation of elements in an array. We can simply use the same decorator, and then the function will also accept pandas DataFrame. Hence all the code to polish input is taken out of these functions by depositing them into the decorator. This is how we can efficiently reuse the code.<\/p>\n<h2>Some Practical Examples of Decorators<\/h2>\n<p>Now that we learned the decorator syntax in Python, let\u2019s see what we can do with it!<\/p>\n<h3>Memoization<\/h3>\n<p>There are some function calls that we do repeatedly, but where the values rarely, if ever, change. This could be calls to a server where the data is relatively static or as part of a dynamic programming algorithm or computationally intensive math function. We might want to <strong>memoize<\/strong> these function calls, i.e., storing the value of their output on a virtual memo pad for reuse later.<\/p>\n<p>A decorator is the best way to implement a memoization function. We just need to remember the input and output of a function but keep the function\u2019s behavior as-is. Below is an example:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import pickle\r\nimport hashlib\r\n\r\n\r\nMEMO = {} # To remember the function input and output\r\n\r\ndef memoize(fn):\r\n    def _deco(*args, **kwargs):\r\n        # pickle the function arguments and obtain hash as the store keys\r\n        key = (fn.__name__, hashlib.md5(pickle.dumps((args, kwargs), 4)).hexdigest())\r\n        # check if the key exists\r\n        if key in MEMO:\r\n            ret = pickle.loads(MEMO[key])\r\n        else:\r\n            ret = fn(*args, **kwargs)\r\n            MEMO[key] = pickle.dumps(ret)\r\n        return ret\r\n    return _deco\r\n\r\n@memoize\r\ndef fibonacci(n):\r\n    if n in [0, 1]:\r\n        return n\r\n    else:\r\n        return fibonacci(n-1) + fibonacci(n-2)\r\n\r\nprint(fibonacci(40))\r\nprint(MEMO)<\/pre>\n<p>In this example, we implemented <code>memoize()<\/code> to work with a global dictionary <code>MEMO<\/code> such that the name of a function together with the arguments becomes the key and the function\u2019s return becomes the value. When the function is called, the decorator will check if the corresponding key exists in <code>MEMO<\/code>, and the stored value will be returned. Otherwise, the actual function is invoked, and its return value is added to the dictionary.<\/p>\n<p>We use <code>pickle<\/code> to serialize the input and output and use <code>hashlib<\/code> to create a hash of the input because not everything can be a key to the Python dictionary (e.g., <code>list<\/code> is an unhashable type; thus, it cannot be a key). Serializing any arbitrary structure into a string can overcome this and guarantee that the return data is immutable. Furthermore, hashing the function argument would avoid storing an exceptionally long key in the dictionary (for example, when we pass in a huge numpy array to the function).<\/p>\n<p>The above example uses <code>fibonacci()<\/code> to demonstrate the power of memoization. Calling <code>fibonacci(n)<\/code> will produce the n-th Fibonacci number. Running the above example would produce the following output, in which we can see the 40th Fibonacci number is 102334155 and how the dictionary <code>MEMO<\/code> is used to store different calls to the function.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">102334155\r\n{('fibonacci', '635f1664f168e2a15b8e43f20d45154b'): b'x80x04Kx01.',\r\n('fibonacci', 'd238998870ae18a399d03477dad0c0a8'): b'x80x04Kx00.',\r\n('fibonacci', 'dbed6abf8fcf4beec7fc97f3170de3cc'): b'x80x04Kx01.',\r\n...\r\n('fibonacci', 'b9954ff996a4cd0e36fffb09f982b08e'): b'x80x04x95x06x00x00x00x00x00x00x00J)pTx02.',\r\n('fibonacci', '8c7aba62def8063cf5afe85f42372f0d'): b'x80x04x95x06x00x00x00x00x00x00x00Jxa2x0exc5x03.',\r\n('fibonacci', '6de8535f23d756de26959b4d6e1f66f6'): b'x80x04x95x06x00x00x00x00x00x00x00Jxcb~x19x06.'}<\/pre>\n<p>You may try to remove the <code>@memoize<\/code> line in the code above. You will find the program takes significantly longer to run (because each function call invokes two more function calls; hence it is running in O(2^n) instead of O(n) as in the memoized case), or you may even be running out of memory.<\/p>\n<p>Memoization is very helpful for expensive functions whose outputs don\u2019t change frequently, for example, the following function that reads some stock market data from the Internet:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n\r\nimport pandas_datareader as pdr\r\n\r\n@memoize\r\ndef get_stock_data(ticker):\r\n    # pull data from stooq\r\n    df = pdr.stooq.StooqDailyReader(symbols=ticker, start=\"1\/1\/00\", end=\"31\/12\/21\").read()\r\n    return df\r\n\r\n#testing call to function\r\nimport cProfile as profile\r\nimport pstats\r\n\r\nfor i in range(1, 3):\r\n    print(f\"Run {i}\")\r\n    run_profile = profile.Profile()\r\n    run_profile.enable()\r\n    get_stock_data(\"^DJI\")\r\n    run_profile.disable()\r\n    pstats.Stats(run_profile).print_stats(0)<\/pre>\n<p>If implemented correctly, the call to <code>get_stock_data()<\/code> should be more expensive the first time and much less expensive subsequently. The output from the code snippet above gives us:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">Run 1\r\n         17492 function calls (17051 primitive calls) in 1.452 seconds\r\n\r\nRun 2\r\n         221 function calls (218 primitive calls) in 0.001 seconds<\/pre>\n<p>This is particularly useful if you are working on a Jupyter notebook. If you need to download some data, wrap it in a memoize decorator. Since developing a machine learning project means many iterations of changing your code to see if the result looks any better, a memoized download function saves you a lot of unnecessary waiting.<\/p>\n<p>You may make a more powerful memoization decorator by saving the data in a database (e.g., a key-value store like GNU dbm or an in-memory database such as memcached or Redis). But if you just need the functionality as above, Python 3.2 or later shipped you the decorator <code>lru_cache<\/code> from the built-in library <code>functools<\/code>, so you don\u2019t need to write your own:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import functools\r\n\r\nimport pandas_datareader as pdr\r\n\r\n# memoize using lru_cache\r\n@functools.lru_cache\r\ndef get_stock_data(ticker):\r\n    # pull data from stooq\r\n    df = pdr.stooq.StooqDailyReader(symbols=ticker, start=\"1\/1\/00\", end=\"31\/12\/21\").read()\r\n    return df\r\n\r\n# testing call to function\r\nimport cProfile as profile\r\nimport pstats\r\n\r\nfor i in range(1, 3):\r\n    print(f\"Run {i}\")\r\n    run_profile = profile.Profile()\r\n    run_profile.enable()\r\n    get_stock_data(\"^DJI\")\r\n    run_profile.disable()\r\n    pstats.Stats(run_profile).print_stats(0)<\/pre>\n<p><strong>Note:<\/strong> The <code>lru_cache<\/code> implements LRU caching, which limits its size to the most recent calls (default 128) to the function. In Python 3.9, there is a <code>@functools.cache<\/code> as well, which is unlimited in size without the LRU purging.<\/p>\n<h3>Function Catalog<\/h3>\n<p>Another example where we might want to consider the use of function decorators is for registering functions in a catalog. It allows us to associate functions with a string and pass the strings as arguments for other functions. This is the start of making a system that will enable user-provided plug-ins. Let\u2019s illustrate this with an example. Below is a decorator and a function <code>activate()<\/code> that we will use later. Let\u2019s assume the following code is saved in the file <code>activation.py<\/code>:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># activation.py\r\n\r\nACTIVATION = {}\r\n\r\ndef register(name):\r\n    def decorator(fn):\r\n        # assign fn to \"name\" key in ACTIVATION\r\n        ACTIVATION[name] = fn\r\n        # return fn unmodified\r\n        return fn\r\n    return decorator\r\n\r\ndef activate(x, kind):\r\n    try:\r\n        fn = ACTIVATION[kind]\r\n        return fn(x)\r\n    except KeyError:\r\n        print(\"Activation function %s undefined\" % kind)<\/pre>\n<p>After defining the <code>register<\/code> decorator in the above code, we can now use it to register functions and associate strings with them. Let\u2019s have the file <code>funcs.py<\/code> as such:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># funcs.py\r\n\r\nfrom activation import register\r\nimport numpy as np\r\n\r\n@register(\"relu\")\r\ndef relu(x):\r\n    return np.where(x&gt;0, x, 0)\r\n\r\n@register(\"sigmoid\")\r\ndef sigm(x):\r\n    return 1\/(1+np.exp(-x))\r\n\r\n@register(\"tanh\")\r\ndef tanh(x):\r\n    return np.tanh(x)<\/pre>\n<p>We\u2019ve registered the \u201crelu,\u201d \u201csigmoid,\u201d and \u201ctanh\u201d functions to their respective strings by building this association in the <code>ACTIVATION<\/code> dictionary.<\/p>\n<p>Now, let\u2019s see how we can use our newly registered functions.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nfrom activation import activate\r\n\r\n# create a random matrix\r\nx = np.random.randn(5,3)\r\nprint(x)\r\n\r\n# try ReLU activation on the matrix\r\nrelu_x = activate(x, \"relu\")\r\nprint(relu_x)\r\n\r\n# load the functions, and call ReLU activation again\r\nimport funcs\r\nrelu_x = activate(x, \"relu\")\r\nprint(relu_x)<\/pre>\n<p>which gives us the output:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">[[-0.81549502 -0.81352867  1.41539545]\r\n [-0.28782853 -1.59323543 -0.19824959]\r\n [ 0.06724466 -0.26622761 -0.41893662]\r\n [ 0.47927331 -1.84055276 -0.23147207]\r\n [-0.18005588 -1.20837815 -1.34768876]]\r\nActivation function relu undefined\r\nNone\r\n[[0.         0.         1.41539545]\r\n [0.         0.         0.        ]\r\n [0.06724466 0.         0.        ]\r\n [0.47927331 0.         0.        ]\r\n [0.         0.         0.        ]]<\/pre>\n<p>Observe that before we reached the <code>import func<\/code> line, the ReLU activation does not exist. Hence calling the function will have the error message print, and the result is <code>None<\/code>. Then after we run that <code>import<\/code> line, we are loading those functions defined just like a plug-in module. Then the same function call gave us the result we expected.<\/p>\n<p>Note that we never invoked anything in the module <code>func<\/code> explicitly, and we didn\u2019t modify anything in the call to <code>activate()<\/code>. Simply importing <code>func<\/code> caused those new functions to register and expanded the functionality of <code>activate()<\/code>. Using this technique allows us to develop a very large system while focusing on only one small part at a time without worrying about the interoperability of other parts. Without the registration decorators and function catalog, adding a new activation function would need modification to <strong>every<\/strong> function that uses activation.<\/p>\n<p>If you\u2019re familiar with Keras, you should resonate the above with the following syntax:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">layer = keras.layers.Dense(128, activation=\"relu\")\r\n\r\nmodel.compile(loss=\"sparse_categorical_crossentropy\",\r\n              optimizer=\"adam\",\r\n              metrics=[\"sparse_categorical_accuracy\"])<\/pre>\n<p>Keras defined almost all components using a decorator of similar nature. Hence we can refer to building blocks by name. Without this mechanism, we have to use the following syntax all the time, which puts a burden on us to remember the location of a lot of components:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">layer = keras.layers.Dense(128, activation=keras.activations.relu)\r\n\r\nmodel.compile(loss=keras.losses.SparseCategoricalCrossentropy(), \r\n              optimizer=keras.optimizers.Adam(),\r\n              metrics=[keras.metrics.SparseCategoricalAccuracy()])<\/pre>\n<\/p>\n<h2>Further reading<\/h2>\n<p>This section provides more resources on the topic if you are looking to go deeper.<\/p>\n<h3>Articles<\/h3>\n<ul>\n<li><a href=\"https:\/\/blogs.oracle.com\/javamagazine\/post\/the-decorator-pattern-in-depth\">Decorator pattern<\/a><\/li>\n<li>Python Language Reference, Section 8.7, <a href=\"https:\/\/docs.python.org\/3\/reference\/compound_stmts.html#function\">Function definitions<\/a>\n<\/li>\n<li><a href=\"https:\/\/peps.python.org\/pep-0318\/\">PEP 318 \u2013 Decorators for Functions and Methods<\/a><\/li>\n<\/ul>\n<h3>Books<\/h3>\n<ul>\n<li>\n<a href=\"https:\/\/www.amazon.com\/dp\/1492056359\/\">Fluent Python<\/a>, 2nd edition, by Luciano Ramalho<\/li>\n<\/ul>\n<h3>APIs<\/h3>\n<ul>\n<li>\n<a href=\"https:\/\/docs.python.org\/3\/library\/functools.html\">functools module<\/a> in Python standard library<\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>In this post, you discovered the decorator design pattern and Python\u2019s decorator syntax. You also saw some specific use cases of decorators that can help your Python program run faster or be easier to extend.<\/p>\n<p>Specifically, you learned:<\/p>\n<ul>\n<li>The idea of a decorator pattern and the decorator syntax in Python<\/li>\n<li>How to implement a decorator in Python for use with the decorator syntax<\/li>\n<li>The use of a decorator for adapting function input and output, for memoization, and for registering functions in a catalog<\/li>\n<\/ul>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/a-gentle-introduction-to-decorators-in-python\/\">A Gentle Introduction to Decorators in Python<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/\">Machine Learning Mastery<\/a>.<\/p>\n<\/div>\n<p><a href=\"https:\/\/machinelearningmastery.com\/a-gentle-introduction-to-decorators-in-python\/\">Go to Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Author: Zhe Ming Chng When working on code, whether we know it or not, we often come across the decorator design pattern. This is a [&hellip;] <span class=\"read-more-link\"><a class=\"read-more\" href=\"https:\/\/www.aiproblog.com\/index.php\/2022\/04\/01\/a-gentle-introduction-to-decorators-in-python\/\">Read More<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":5529,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[24],"tags":[],"_links":{"self":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/5528"}],"collection":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/comments?post=5528"}],"version-history":[{"count":0,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/5528\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media\/5529"}],"wp:attachment":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media?parent=5528"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/categories?post=5528"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/tags?post=5528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}