Reputation: 5555
I have the following decorator example:
def decorator_function1(original_function):
def wrapper_function1(*args, **kwargs):
print('wrapper executed this before {}'.format(original_function.__name__))
return original_function(*args, **kwargs)
return wrapper_function1
@decorator_function1
def display_info(name, age):
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('John', 25)
what I am struggling with the line: def wrapper_function1(*args, **kwargs):
even after reading a lot of decorator tutorials
I do not understand why * and ** is needied in the def wrapper_function1() at all if it is coming in here return original_function(*args, **kwargs). Isn't that redundant?
If I leave * and ** in def wrapper_function1()
out I get the error: TypeError: wrapper_function1() takes 0 positional arguments but 2 were given
but why does wrapper_function1 knows about the arguments of display info
? I just cannot see where they are handed over. Something like this would make sense to me
def decorator_function1(original_function, *args, **kwargs)): my_args = args my_kwargs = kwargs def wrapper_function(args, kwargs) ....
Upvotes: 0
Views: 71
Reputation: 5033
Unfortunately, your example to understand decorators is "born bad"... decorator_function1
can be used as a decorator but its signature is not compatible with the syntactic sugar notation, the @
syntax, but only following the order of the nested calls, here a possible example decorator_function1(display_info)()('John', 25)
.
The *
and **
acts as unpacking operators for list and dictionary respectively. Why are useful? Because you don't have to care about the amount of parameters which may be different because the functions that you want to decorate may have not all the same signature, docs.
the error arises because the decorator signature is not compatible with the @
notation, to fix it you have to swap the parameters of the decorator with those of the wrapper. Here, an exhausting list on how to do that with focus on the role of the args
and kwargs
.
def decorator_function1(*args, **kwargs):
def wrapper_function1(original_function):
print('wrapper executed this before {}'.format(original_function.__name__))
return lambda *aargs, **kkwargs: original_function(*args + aargs, **kwargs | kkwargs)
return wrapper_function1
# parameters: original func only
@decorator_function1()
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info(1, 2))
# parameters: decorator only
@decorator_function1(1, 2)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info())
# parameters: positional mix - order depending!
@decorator_function1(1)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info(2))
# parameters: positional mix - order depending!
@decorator_function1(2)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info(1))
# parameters: key-pairs decorator only
@decorator_function1(age=1, name=2)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info())
# parameters: key-pairs original func only
@decorator_function1()
def display_info(name=1, age=2):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info())
# parameters: key-pairs mix
@decorator_function1(age=1)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info(name=2))
# parameters: key-pairs mix
try:
@decorator_function1(name=1)
def display_info(name, age):
return 'display_info ran with arguments ({}, {})'.format(name, age)
print(display_info(2))
except TypeError as e:
print(e)
Upvotes: 1
Reputation: 7539
Because writing this
@decorator_function1
def display_info(name, age):
# some code
Is just "syntatic sugar" for this:
def display_info(name, age):
# some code
display_info = decorator_function1(display_info)
Which is how the function display_info
now knows about decorator_function
.
Upvotes: 0