📜 ⬆️ ⬇️

Understand the decorators in Python'e, step by step. Step 1


On Habré, the theme of decorators was discussed many times, however, in my opinion, this article (which grew out of a single question on stackoverflow ) describes this topic most clearly and, importantly, is a “step-by-step guide” on the use of decorators, allowing the novice to master this technique immediately decent level.

So, what is a "decorator"?


There is a fairly long article ahead, so if someone is in a hurry, this is an example of how decorators work:
def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello habr" print hello() ##  <b><i>hello habr</i></b> 


Those of you who are willing to spend some time are invited to read the long post.

Functions in Python are objects

In order to understand how decorators work, one should first of all realize that in Python functions are also objects.
Let's see what follows from this:
 def shout(word=""): return word.capitalize()+"!" print shout() # : '!' #    -  ,     , #      scream = shout # ,     :     "shout", #      "scream".  ,    #   "shout"  "scream": print scream() # : '!' #  ,  ,     "shout",     #     "scream" del shout try: print shout() except NameError, e: print e #: "name 'shout' is not defined" print scream() # : '!' 

Let us remember this fact, we will return to it soon, but in addition, it should be understood that the function in Python can be defined ... inside another function!

 def talk(): #    "talk"    ... def whisper(word=""): return word.lower()+"..."; # ...     ! print whisper() # ,     "talk",      #    "whisper". talk() # : "..." #    "talk"     "whisper": try: print whisper() except NameError, e: print e # : "name 'whisper' is not defined" 

Feature Links

Are you still here? :)
')
Now we know that functions are full objects, which means:

Well, which means that one function can return another function!
Let's get a look:
 def getTalk(type="shout"): #      def shout(word=""): return word.capitalize()+"!" def whisper(word="") : return word.lower()+"..."; #    if type == "shout": # ,     "()",     , #     return shout else: return whisper #     ? #        talk = getTalk() #    , "talk"  -  "function": print talk # : <function shout at 0xb7ea817c> #   ,   ,  " ": print talk() #    -       : print getTalk("whisper")() # : ... 

Wait, since we can return a function, it means that we can also transfer it to another function as a parameter:
 def doSomethingBefore(func): print "  - ,     ,    " print func() doSomethingBefore(scream) #: #   - ,     ,     # ! 

Well, now we have all the necessary knowledge in order to understand how decorators work.
As you might have guessed, decorators are, in essence, just peculiar “wrappers” that enable us to do something before and after what the decorated function will do without changing it.

Create your decorator "manually"

 #  -  ,       def my_shiny_new_decorator(a_function_to_decorate): #     -"". #   (   ?..)   , #         . def the_wrapper_around_the_original_function(): #   ,       #   print " - ,     " #     a_function_to_decorate() #    ,       #   print "  - ,  " #     "a_function_to_decorate"     # ,  -,     #  ,  ,      . #  ! return the_wrapper_around_the_original_function #  ,     ,      . def a_stand_alone_function(): print "   ,      ?.." a_stand_alone_function() # :    ,      ?.. # ,    ,    ,   #   ,       , #   ,   ,    : a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function) a_stand_alone_function_decorated() #: #  - ,      #    ,      ?.. #   - ,   

Probably, now we would like, that each time, during a call a_stand_alone_function , instead of it a_stand_alone_function _decorated was called . There is nothing easier, just rewrite a_stand_alone_function function, which was returned to us by my_shiny_new_decorator :
 a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function) a_stand_alone_function() #: #  - ,      #    ,      ?.. #   - ,   

You guessed it, it’s exactly the same thing as the @ decorators do. :)

We destroy the aura of mystery around decorators

This is how it was possible to write the previous example using the syntax of decorators:
 @my_shiny_new_decorator def another_stand_alone_function(): print "   " another_stand_alone_function() #: #  - ,      #     #   - ,   

Yes, everything is really that simple! decorator - just syntactic sugar for constructions of the form:
 another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function) 

Decorators are just a pythonic implementation of the “Decorator” design pattern . Python includes some classic design patterns , such as the decorators covered in this article, or iterators familiar to any paytonist.

Of course, you can put decorators into each other, like this:
 def bread(func): def wrapper(): print "</------\>" func() print "<\______/>" return wrapper def ingredients(func): def wrapper(): print "##" func() print "~~" return wrapper def sandwich(food="----"): print food sandwich() #: ---- sandwich = bread(ingredients(sandwich)) sandwich() #: # </------\> # ## # ---- # ~~ # <\______/> 


And using the decorators syntax:
 @bread @ingredients def sandwich(food="----"): print food sandwich() #: # </------\> # ## # ---- # ~~ # <\______/> 


It should be remembered that the order of decoration IMPORTANT:
 @ingredients @bread def sandwich(food="----"): print food sandwich() #: # ## # </------\> # ---- # <\______/> # ~~ 


At this moment you can happily leave, with the knowledge that you understood what decorators are and what they eat with.
For those who want to torture their brain a little more, the second part of the article devoted to the advanced use of decorators will be translated tomorrow.

Content:

Note translator:
Thanks for attention. I would be grateful for any comments on the translation and design, I will try to take them all into account in the second part of the translation.
UPD: In the second part, issues such as: passing arguments to decorating functions, decorating methods, decorators with parameters, etc. will be discussed.
UPD2: The second part of the article is posted .

Source: https://habr.com/ru/post/141411/


All Articles