{"id":219,"date":"2014-05-23T00:37:34","date_gmt":"2014-05-23T00:37:34","guid":{"rendered":"http:\/\/96.126.106.214\/?p=219"},"modified":"2023-11-26T01:00:13","modified_gmt":"2023-11-26T01:00:13","slug":"python-decorators-basics","status":"publish","type":"post","link":"https:\/\/codingismycraft.blog\/index.php\/2014\/05\/23\/python-decorators-basics\/","title":{"rendered":"Python Decorators Basics"},"content":{"rendered":"<p><iframe loading=\"lazy\" src=\"\/\/www.youtube.com\/embed\/bxhuLgybIro\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><br \/>\n<strong>Introduction to decorators<\/strong><\/p>\n<p>Decorators introduce a programming paradigm that is not found in statically linked languages, like C++ or Java. Taking advantage of closures and the fact that functions are first class citizens of python, we can easily change the behaviour of them adding a single line of code.<\/p>\n<p>If you have not used dynamic languages before, chances are that you cannot immediately see the value of decorators but as you will learning about them chances are that you are going to change the way you write and think about coding, as you will discover more elegant and efficient ways to implement things that are either very verbose or even impossible to code in traditional statically linked languages. Although there is a small learning curve associated with them, decorators will quickly pay back enough dividend to justify the learning process.<\/p>\n<p>A python decorator is nothing else than a callable object, receiving a callable as a parameter and returning another callable as its return value.<\/p>\n<p>We will start with the simplest possible case of a decorator, which consists of a regular function accepting a function as its parameter, defining a nested function taking the same parameters as the passed one, returning the nested function to the caller.<\/p>\n<p>What I have just described above, can be visualized in the following picture which presents the basic blocks of a python decorator:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-492\" src=\"http:\/\/23.92.19.129\/wp-content\/uploads\/2014\/05\/decorator-summary.jpg\" alt=\"decorator-summary\" \/><\/p>\n<p>In the following page we will delve in a bit deeper to the concept of decorators, writing a very simplistic program presenting their use.<\/p>\n<p><!--nextpage--><\/p>\n<p><strong>A simple program using decorators<\/strong><\/p>\n<p>The following is a simple program using a decorator:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n# A very simple decorator demonstrating the basic concept\r\n\r\ndef timestamp():\r\nimport time\r\nimport datetime\r\nts = time.time()\r\nmask = &#039;%Y-%m-%d %H:%M:%S&#039;\r\nreturn datetime.datetime.fromtimestamp(ts).strftime(mask)\r\n\r\ndef add_time_stamp(foo):\r\n&#039;&#039;&#039; add_time_stamp is a decorator accepting a function foo &#039;&#039;&#039;\r\ndef inner(*args, **kargs):\r\n&#039;&#039;&#039;\r\ninner is the function containing the decoration\r\nwhich in this case consists of printing the timestamp\r\nand the calling the outer function\r\n&#039;&#039;&#039;\r\nprint timestamp(),\r\nfoo(*args, **kargs)\r\nreturn inner\r\n\r\n@add_time_stamp\r\ndef show_msg(msg):\r\n&#039;&#039;&#039;\r\nshow_msg takes a single parameter as its argument and prints\r\nit to the screen.\r\n\r\nNote hat he definition of show_msg is preceeded by @add_time_stamp\r\nsomething that is equivalent to the following:\r\n\r\ndef show_msg(msg): print msg\r\nshow_msg = add_time_stamp(show_msg)\r\n&#039;&#039;&#039;\r\nprint msg\r\n\r\nif __name__ == &quot;__main__&quot;:\r\nshow_msg(&#039;test&#039;)\r\n<\/pre>\n<p>Running this program from the command line, you will see the following output:<\/p>\n<pre>2014-05-15 18:04:36 test<\/pre>\n<p>As we can see, we are getting the current time-stamp followed by the string we passed (&#8216;test&#8217; in our case) printed in the standard output. The implementation of show_msg knows nothing about the time-stamp, is the decorator that is explicitly print the time-stamp and then calls the original function.<\/p>\n<p>What is interesting in this case though, is the little magic that is going on with the use of the @add_time_stamp right before the definition of show_msg. The &#8220;at&#8221; sign (@) that goes before the add_time_stamp is a syntactic sugar, saying to python that we want to use the function who&#8217;s name is printed right after it to decorate the function that will be defined in the immediately next line!<\/p>\n<p>The following lines of code:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n@add_time_stamp\r\ndef show_msg(msg):\r\nprint msg\r\n<\/pre>\n<p>are exactly equivalent to the following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n\r\ndef show_msg(msg):\r\nprint msg\r\n\r\nshow_msg = add_time_stamp(show_msg)\r\n\r\n<\/pre>\n<p>The use of the ampersand can be viewed as a syntactic sugar that simplifies the way we are writing the code, making it less verbose and more expressive. Although it might seem like a simple facility, this mechanism very useful and in many cases changes completely the way we write code, something that will become more evident as you start using this approach more and more..<\/p>\n<p>Note that our decorator will work with any function regardless of the number of parameters it takes, because the inner function is using the following signature:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef inner(*args, **kargs):\r\n<\/pre>\n<p>keeping itself transparent to the specific signature of the function that is been decorated.<\/p>\n<p>At this point you know enough to start writing some more useful snippets that will use decorators in your programs, to see an example keep on reading the next page&#8230;<\/p>\n<p><!--nextpage--><\/p>\n<p><strong>Using decorators to write a generic exception handler<\/strong><\/p>\n<p>Let&#8217;s write a realistic example of the use of decorators trying to improve our understanding of them.<\/p>\n<p>In the following program we define a function called divide which is implement the trivial functionality of dividing the two parameters that are passed to it:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef divide(x,y):\r\nreturn x \/ y\r\n<\/pre>\n<p>Nothing fancy or useful of course, but enough to make apparent the use of decorator as we will see here.<\/p>\n<p>If the divide function is called with y equalling 0 obviously it will throw an exception as we can see here:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\ndef divide(x,y):\r\nreturn x \/ y\r\n\r\nprint divide(8,0)\r\n<\/pre>\n<p>Running this program will result to the following output:<\/p>\n<pre>Traceback (most recent call last):\r\n  File \".\/junk1.py\", line 6, in \r\n    print 8\/0\r\nZeroDivisionError: integer division or modulo by zero<\/pre>\n<p>The obvious way to fix this behaviour is to catch this exception showing some message to the user:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\ndef divide(x,y):\r\ntry:\r\na = x\/y\r\nreturn x \/ y\r\nexcept Exception as ex:\r\nprint ex\r\n\r\nprint divide(8,0)\r\n<\/pre>\n<p>To make our example more realistic, lets assume that we need another function requiring similar exception catching behaviour, something like this:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\ndef print_file(filename):\r\ntry:\r\nf = open(filename)\r\nprint f.read()\r\nexcept Exception as ex:\r\nprint ex\r\n\r\ndef divide(x,y):\r\ntry:\r\na = x\/y\r\nreturn x \/ y\r\nexcept Exception as ex:\r\nprint ex\r\n\r\nprint_file(&#039;nonexistent_file&#039;)\r\n<\/pre>\n<p>In this case, assuming nonexistent_file indeed does not exist, we except to see the following output when we run the program:<\/p>\n<pre>[Errno 2] No such file or directory: 'nonexistent_file'<\/pre>\n<p>Sure, we have a working program that can handle possible exceptions reasonably well but as you can see the same code needed to handle them is repeated in both functions. The use of a simple decorator can make our code more expressible and easier to understand and this is how this can be done:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\ndef handle_exceptions(foo):\r\ndef inner(*args, **kargs):\r\ntry:\r\nfoo(*args, **kargs)\r\nexcept Exception as ex:\r\nprint ex\r\nreturn inner\r\n\r\n@handle_exceptions\r\ndef print_file(filename):\r\nf = open(filename)\r\nprint f.read()\r\n\r\n@handle_exceptions\r\ndef divide(x,y):\r\na = x\/y\r\nreturn x \/ y\r\n\r\nprint_file(&#039;nonexistent_file&#039;)\r\n<\/pre>\n<p>Note, how our code is now cleaner as the print_file and divide functions are no longer polluted with the exception handling logic which is now implemented in a decorator making our code easier to read and understand.<\/p>\n<p>One question that arises here, is what if did not want one of the functions to actually display the exception message? How in other words we can customize the decorator passing to it additional parameters to alter its behaviour? This will be the topic of the next example that you can read in the following page.<\/p>\n<p><!--nextpage--><\/p>\n<p><strong>Adding parameters to decorators<\/strong><\/p>\n<p>In the previous page we simplified our code by using a decorator to handle exceptions keeping clear the implementation of a function. This is a great improvement over repeating the same code over and over for each function we write but someone might make the case that this solution is very generic and we might need more control over the implementation of the decorator to specify its behaviour.<\/p>\n<p>This can be accomplished by a decorator with parameters which is slightly more complicated than a parameter-less one and it can be done by nesting our current decorator within a higher level function which will now become the real decorator.<\/p>\n<p>Continuing our example, let&#8217;s assume that we can solve our problem by using a boolean called that will either make our handler to print the message or not. Based on this our decorator should now look like this:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef handle_exceptions(foo):\r\ndef inner(*args, **kargs):\r\ntry:\r\nfoo(*args, **kargs)\r\nexcept Exception as ex:\r\nif print_message:\r\nprint ex\r\nreturn inner\r\nreturn handle_exceptions\r\n<\/pre>\n<p>The question now is, how we can pass the print_message boolean that is used in the exception handler. Very easy! We just wrap our function within a higher level function which takes a single parameter print_message and now this new function becomes our decorator. This can be seen in the following snippet:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef handle_exceptions_ex(print_message):\r\ndef handle_exceptions(foo):\r\ndef inner(*args, **kargs):\r\ntry:\r\nfoo(*args, **kargs)\r\nexcept Exception as ex:\r\nif print_message:\r\nprint ex\r\nreturn inner\r\nreturn handle_exceptions\r\n<\/pre>\n<p>Now, the decorator part for our functions should change to the following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n@handle_exceptions_ex(True)\r\ndef print_file(filename):\r\n<\/pre>\n<p>and<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n@handle_exceptions_ex(True)\r\ndef divide(x,y):\r\n<\/pre>\n<p>the whole program should look like this now:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\ndef handle_exceptions_ex(print_message):\r\ndef handle_exceptions(foo):\r\ndef inner(*args, **kargs):\r\ntry:\r\nfoo(*args, **kargs)\r\nexcept Exception as ex:\r\nif print_message:\r\nprint ex\r\nreturn inner\r\nreturn handle_exceptions\r\n\r\n@handle_exceptions_ex(True)\r\ndef print_file(filename):\r\nf = open(filename)\r\nprint f.read()\r\n\r\n@handle_exceptions_ex(True)\r\ndef divide(x,y):\r\na = x\/y\r\nreturn x \/ y\r\n\r\nprint_file(&#039;nonexistent_file&#039;)\r\n<\/pre>\n<p>In this posting we covered the basics of python decorators. There are more advanced uses of them that we will talk about in the next postings.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction to decorators Decorators introduce a programming paradigm that is not found in statically linked languages, like C++ or Java. Taking advantage of closures and&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-219","post","type-post","status-publish","format-standard","hentry","category-programming"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":30,"url":"https:\/\/codingismycraft.blog\/index.php\/2013\/03\/11\/python-is-great\/","url_meta":{"origin":219,"position":0},"title":"Python is great","author":"john","date":"March 11, 2013","format":false,"excerpt":"I have fallen in love with several programming languages in the past, with C++ being the most dominate of them. During the last years though, I have become a Python fan boy, trying to use it as much as I can, something that translates to use it almost everywhere except\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":279,"url":"https:\/\/codingismycraft.blog\/index.php\/2024\/10\/03\/the-challenges-of-python-migration-lessons-from-c-and-beyond\/","url_meta":{"origin":219,"position":1},"title":"The Challenges of Python Migration: Lessons from C++ and Beyond","author":"john","date":"October 3, 2024","format":false,"excerpt":"One project that confirmed the need for caution and conservatism when estimating deadlines involved migrating a massive codebase of over 4,000 Python files and 250+ open-source libraries from Python 3.6 to 3.10. What was initially seen as a straightforward task, expected to take just a few weeks, ended up consuming\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":66,"url":"https:\/\/codingismycraft.blog\/index.php\/2016\/03\/10\/the-three-dimensions-of-a-programmer\/","url_meta":{"origin":219,"position":2},"title":"The three dimensions of a programmer.","author":"john","date":"March 10, 2016","format":false,"excerpt":"\u00a0 It is my impression that the vast majority of new developers, tend to underestimate the importance of design and implementation details as they struggle to prove that they can deliver a solution very quickly and please their managers with very tight deadlines. A developer eager to prove his abilities,\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":217,"url":"https:\/\/codingismycraft.blog\/index.php\/2014\/05\/26\/interface-driven-programming-in-python\/","url_meta":{"origin":219,"position":3},"title":"Interface Driven Programming In Python","author":"john","date":"May 26, 2014","format":false,"excerpt":"If it looks like a duck, quacks like a duck and walks like a duck, it's a duck Probably the greatest feature of python is its dynamic nature.\u00a0 By this we mean that variable names are not bound to types and they also can be assigned at run time, this\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":213,"url":"https:\/\/codingismycraft.blog\/index.php\/2017\/10\/04\/adding-descriptors-to-your-python-arsenal\/","url_meta":{"origin":219,"position":4},"title":"Adding descriptors to your python arsenal","author":"john","date":"October 4, 2017","format":false,"excerpt":"Python\u00a0provides a\u00a0 rich programming paradigm providing the ability to the programmer to express his solutions in a very concise and elegant way.\u00a0 One of the python features that is not in wide use although it gives the opportunity to simplify certain pieces of code,\u00a0is the descriptor protocol which is the\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":35,"url":"https:\/\/codingismycraft.blog\/index.php\/2013\/03\/11\/programming-languages\/","url_meta":{"origin":219,"position":5},"title":"Programming Languages","author":"john","date":"March 11, 2013","format":false,"excerpt":"As a seasoned programmer I have used a wide spectrum of programming languages and development approaches. By the end of the 80's I was introduced to the concepts of Object Oriented Programming using as main language the C++ which I still follow very closely. As a heavy user of Visual\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/comments?post=219"}],"version-history":[{"count":1,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/219\/revisions"}],"predecessor-version":[{"id":220,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/219\/revisions\/220"}],"wp:attachment":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/media?parent=219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/categories?post=219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/tags?post=219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}