Reputation: 59
when we write function inside function why we don't call it ? how the return call it ?
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
why return wrapper instead of return wrapper() ? and what we do when wrapper must take arguments?
Upvotes: 1
Views: 879
Reputation: 18625
A decorator is used to replace a function with a different one, usually one that modifies the behavior of the original function in some way. So the decorator has to return a function that should be called instead of the original one.
If you returned wrapper()
, you would just call the new wrapper
function once and return the result. But what you really need to do is replace func
with wrapper
, so that every time someone calls func
, they'll actually call wrapper
instead, which will then call func
as needed. So my_decorator
has to return wrapper
itself, not the output from wrapper()
. Then, when you use my_decorator
as a decorator, Python will automatically call wrapper
everytime instead of the original function.
The wrapper
function should always take the same arguments as func
. So if it needs arguments, just define it with the same arguments as func
takes in the def wrapper():
line.
Upvotes: 1
Reputation: 3120
Functions can be treated like values in Python
def foo():
print("Hello World!")
x = foo
x()
is valid Python that will print out Hello World!
Expanding on this idea... What if we wanted to do something before we called our function? Well we could do this:
def foo():
print("Hello World!")
def do_something_before():
print("something before")
foo()
x = do_something_before
x()
But we have locked ourselves in to only being able to use the function foo. What if we wanted to do "something before" for bar
or foobar
or any number of functions?
We parameterize it! And since we are adding extra bits onto the "normal" function, i.e decorating it, let's call this new function a decorator.
def foo():
print("Hello World!")
def do_something_before(func): # the decorating function
def wrap():
print("something before")
func()
return wrap
x = do_something_before(foo)
x()
Python then allows us to use syntactic sugar to do this wherever we want:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def foo():
print("Hello World!")
Decorators have to take a function in, then be allowed to be called again so instead of directly calling foo we will return the function with foo inside of it to call later when we call foo. We are actually calling wrapper, returned by foo. If we called wrapper
in my decorator, we would be returning the results of wrapper
rather than wrapper
itself. That means we couldn't call foo()
later as there is essentially no function to call.
We can add arguments like this:
def my_decorator(func):
def wrapper(*args):
print("Something is happening before the function is called.")
func(*args)
print("Something is happening after the function is called.")
return wrapper
Meaning my_decorator
can now decorate any function. We could also specify a specific number of arguments.
Upvotes: 2