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.