{"id":241,"date":"2023-12-30T10:31:35","date_gmt":"2023-12-30T16:31:35","guid":{"rendered":"https:\/\/www.baizhao666.com\/?p=241"},"modified":"2024-07-17T18:44:12","modified_gmt":"2024-07-18T00:44:12","slug":"decorator","status":"publish","type":"post","link":"https:\/\/www.baizhao666.com\/?p=241","title":{"rendered":"Decorator"},"content":{"rendered":"\n<p>Decorators in Python are functions that modify other functions. If we need to modify a lot of functions all at once with a minimum of coding, we should use a decorator. A decorator allows us to modify the behavior of a function without changing the codes inside of the function permanently.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>A decorator is defined just like a function, but it must take another function as input argument. The decorator at the end must return the modified function. To modify the function, the typical way to this is to define the new function inside the decorator. The new function inside the decorator is sometimes called a &#8220;wrapper&#8221;.<\/p>\n\n\n\n<p>Suppose we have made a bunch of one-variable functions that simply &#8220;return&#8221; some output. But somehow later we actually want those functions to &#8220;print&#8221; the output instead. In this case, we can make just make a decorator that modifies functions to accomplish this for all functions at once instead of changing the codes in every function.<\/p>\n\n\n\n<p>Suppose the functions that we made look like this:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code># function that takes one variable and return some output\ndef my_func(x):\n    return (x + 3) ** 2<\/code><\/pre><\/div>\n\n\n\n<p>And a decorator that prints the output instead of returning:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code># a decorator definition\ndef my_dec(fun):\n    def wrapper(x):\n        print(fun(x))\n    return wrapper<\/code><\/pre><\/div>\n\n\n\n<p>Notice that a decorator must take a function as input argument, and define a new function that modifies that original function. In the end, the decorator must return the name of the function (<strong>NOT<\/strong> <strong>INCLUDE<\/strong> any arguments). What&#8217;s more, the decorator must be above the function that is being applied.<\/p>\n\n\n\n<p>To apply a decorator to a function definition, simply put @&lt;name of decorator&gt; right above the &#8220;def&#8221;. When we call the original function, the behavior has been changed.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>@my_dec\ndef my_func(x):\n    return (x + 3) ** 2\n\nmy_func(3)<\/code><\/pre><\/div>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">36\n<\/pre>\n\n\n\n<p>The number of input argument of new function inside a decorator is not necessarily match the original function. Instead, when we call the function that has decorator, the input arguments need to match the new function inside the decorator. For example:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code># a wrapper that takes no input argument\ndef take_input(fun):\n    def wrapper():\n        x = eval(input(&quot;Please enter a number: &quot;))\n        print(fun(x))\n    return wrapper\n\n# a function take takes one input argument\n@take_input\ndef my_func(x):\n    return (x + 3) ** 2\n\n\nmy_func()<\/code><\/pre><\/div>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Please enter a number: 5\n64\n<\/pre>\n\n\n\n<p>We can apply multiple decorators to a function. But we need to pay attention to the order of decorators (<strong>ORDER MATTERS<\/strong>). <\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code># a decorator that prints some logs\ndef logger(fun):\n    def wrapper():\n        print(&quot;before the function&quot;)\n        fun()\n        print(&quot;after the function&quot;)\n    return wrapper\n\n# a decorator that runs the function twice\ndef do_twice(fun):\n    def wrapper():\n        fun()\n        fun()\n    return wrapper\n\n# the closer decorator will be applied earlier\n@logger\n@do_twice\ndef new_func():\n    print(&quot;hello world&quot;)<\/code><\/pre><\/div>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">before the function\nhello world\nhello world\nafter the function\n<\/pre>\n\n\n\n<p>To make our decorator as flexible as possible to handler all inputs, use *args and **kwargs to allow for arbitrarily many arguments and keyword arguments. <\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>def error_handler(fun):\n    def wrapper(*args, **kwargs):\n        try:\n            return fun(*args, **kwargs)\n        except Exception as e:\n            return f&quot;An error occurs! The error is {e}&quot;\n    return wrapper\n\n\n@error_handler\ndef division(a, b):\n    return a \/ b<\/code><\/pre><\/div>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">2.5\nAn error occurs! The error is division by zero\n<\/pre>\n\n\n\n<p>Decorators can be used for all sort of things, like graphing or debugging or timing your functions or suppressing\/handling errors or printing output, etc. Another common used of a decorator is timing our functions. For example:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>def time_dec(fun):\n    def wrapper(*args, **kwargs):\n        start = time.time()\n        out = fun(*args, **kwargs)\n        end = time.time()\n        print(f&quot;It took {end - start} seconds for {fun.__name__} to finish&quot;)\n        return out\n    return wrapper\n\n\n@time_dec\ndef slow_fun(n):\n    ret = 0\n    for i in range(n ** n):\n        ret += 1\n        ret -= 1\n    return ret\n\n\nslow_fun(9)<\/code><\/pre><\/div>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">It took 11.597818851470947 seconds for slow_fun to finish\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Decorators in Python are functions that modify other functions. If we need to modify a lot of functions all at once with a minimum of coding, we should use a decorator. A decorator allows us to modify the behavior of a function without changing the codes inside of the function permanently.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-241","post","type-post","status-publish","format-standard","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/posts\/241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=241"}],"version-history":[{"count":2,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/posts\/241\/revisions"}],"predecessor-version":[{"id":270,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=\/wp\/v2\/posts\/241\/revisions\/270"}],"wp:attachment":[{"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=241"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=241"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.baizhao666.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}