Decorators

Trying to make a small change to a python program in my job, I came across something I didnt know in python and only after reading this article (a couple of times) I managed to understand (ask me again in 3 weeks 🙂 The best definition I could find was actually from other blog reference and actually I think it was more clear to me.

“A decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.”

1 – Function returning a function:

In [7]: def greet(name): 
   ...:     return f"Hello, {name}!" 
   ...: def simon(func): 
   ...:     return func("Simon") 
   ...:                                                                                                                       

In [8]: simon(greet)                                                                                                          
Out[8]: 'Hello, Simon!'

simon is a function that has a function as argument, and calls that function with the argument “Simon”. So the function greet is receiving the argument “Simon” and returns a string with that name.

2 – Functions inside other functions:

In [9]: def respect(maybe): 
   ...:     def congrats(): 
   ...:         return "Congrats, bro!" 
   ...:     def insult(): 
   ...:         return "You're silly!" 
   ...:     if maybe == "yes": 
   ...:         return congrats 
   ...:     else: 
   ...:         return insult 
   ...:                                                                                                                       

In [10]: respect("hola")()                                                                                                    
Out[10]: "You're silly!"

In this case, to execute the returned function you need to use “()” at the end (because respect(“hola”) it is a function!): respect(“hola”)()

Keep in mind you return the function “congrats” and it is not executed. If you return “congrats()” then yes, you get the result of executing congrats.

3 – A function that takes another function and defines a function:

In [1]: def startstop(func): 
   ...:     def wrapper(): 
   ...:         print("Starting...") 
   ...:         func() 
   ...:         print("Finished!") 
   ...:     return wrapper 
   ...: def roll(): 
   ...:     print("Rolling on the floor laughing XD") 
   ...: roll = startstop(roll)                                                                                                

In [2]: roll()                                                                                                                
Starting...
Rolling on the floor laughing XD
Finished!

In [3]:        

This case is the same as before, need to use “()” to “execute” roll because it is a function.

4 – With decorator:

In [3]: def startstop(func): 
   ...:     def wrapper(): 
   ...:         print("Starting...") 
   ...:         func() 
   ...:         print("Finished!") 
   ...:     return wrapper 
   ...: @startstop 
   ...: def roll(): 
   ...:     print("Rolling on the floor laughing XD") 
   ...:                                                                                                                       

In [4]: roll()                                                                                                                
Starting...
Rolling on the floor laughing XD
Finished!

In [5]:          

So adding the decorator we avoid writing a line with nested functions. That in a more advanced case, it would make the code easier to read.

You can nest decorators and add arguments too.