I am currently trying to understand what decorators are and what they are used for. Until now, I've learned that they are just functions which pass and return functions as arguments and their purpose is to modify a function w/o modifying the original function.
My question is related to the classic structure used to define them. Usually you define a decorator function and within it, another function called wrapper which is returned by the decorator function.
So my question is, why is wrapper created inside the decorator when it could be done with just a function? Would`t it be more "pythonic" to reduce code side by doing so?
As an example, the following 2 pieces of code:
def country(func):
def wrapper_function():
print("The name of my favorite country is: ")
return func()
return wrapper_function
@country # == fav_country = country(fav)
def fav():
print("Sweden")
fav()
OUTPUT:
The name of my favorite country is:
Sweden
def country(func):
print("The name of my favorite country is: ")
return func
@country # == fav_country = country(fav)
def fav():
print("Sweden")
fav()
OUTPUT: The name of my favorite country is:
Sweden
You are correct that a decorator is nothing else then a function taking a function as an argument and returning a function. But these two:
def country1(func):
def wrapper_function():
print("The name of my favorite country is: ")
return func()
return wrapper_function
def country2(func):
print("The name of my favorite country is: ")
return func
are not equivalent. Consider
@country1
def fav1():
print("Sweden")
@country2
def fav2():
print("Sweden")
and see what happens when you call
fav1()
fav1()
fav1()
and
fav2()
fav2()
fav2()
Your country2
doesn't modify the original function and the result is The name of my favorite country is:
printed only once during decoration process (at function declaration). While country1
changes the behaviour of the decorated function, you will see the message three times.
// EDIT: Decorators are just syntactic sugar. The following two snippets are more or less equivalent
@my_decorator
def test():
...
and
def test():
...
test = my_decorator(test)
In particular we can do weird things with decorators, e.g. they don't have to return functions. For example try this:
def my_decorator(func):
return 5
@my_decorator
def test():
print("test")
and see what happens. The original function under test
symbol is lost, and the test
symbol now points to 5
.