Error occuring when I try to run decorator with @ – Python
Question:
I am having a problem with the following programm.
When I try to run decorator the easier way, using @ like this
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun + 'n' + text
return wrapper()
def decorator2(fun):
def wrapper():
return fun.upper()
return wrapper()
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())
Following problems occurs:
Traceback (most recent call last):
File "C:Python_projectsmain.py", line 17, in <module>
def function():
File "C:Python_projectsmain.py", line 13, in decorator2
return wrapper()
File "C:Python_projectsmain.py", line 11, in wrapper
return fun.upper()
AttributeError: 'function' object has no attribute 'upper'
or when I switch the order of decorators then it goes like this:
Traceback (most recent call last):
File "C:Python_projectsmain.py", line 17, in <module>
def function():
File "C:Python_projectsmain.py", line 6, in decorator1
return wrapper()
File "C:Python_projectsmain.py", line 4, in wrapper
return text + 'n' + fun + 'n' + text
TypeError: can only concatenate str (not "function") to str
When I run the code in this way, then it works just fine:
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun + 'n' + text
return wrapper()
def decorator2(fun):
def wrapper():
return fun.upper()
return wrapper()
def function():
return "Hey ya!"
print(decorator(decorator2(function())))
But it seems like using @ with decorators is much more popular. Do you have any idea what I am doing wrong?
Answers:
First of all, a decorator is a function that accepts a function and returns a function. Thus, do not call the wrapper
, but return it. Moreover, the fun
method should be called.
def decorator1(fun):
def wrapper():
text = "------"
return text + "n" + fun() + "n" + text
return wrapper
def decorator2(fun):
def wrapper():
return fun().upper()
return wrapper
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())
Note that those decorators will work only with functions (or callables in general) that return str
and accept no arguments. Additionally, you can annotate the fun
argument properly with Callable[[], str]
:
from typing import Callable
def decorator1(fun: Callable[[], str]):
...
A decorator is a function which is run on another function, as in a function object, not the result of another function.
So:
@decorator
def func():
...
Is equivalent to:
def func():
...
func = decorator(func)
Notice that the function func
has never actually been called.
Therefore, within the wrapper function within the decorator, you actually need to call the function yourself. Use, for example, fun().upper()
, not fun.upper()
.
Also, the wrapper function should never be called within the decorator; the function itself should be returned, so return wrapper
not return wrapper()
.
Here is the corrected code:
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun() + 'n' + text
return wrapper
def decorator2(fun):
def wrapper():
return fun().upper()
return wrapper
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())
I am having a problem with the following programm.
When I try to run decorator the easier way, using @ like this
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun + 'n' + text
return wrapper()
def decorator2(fun):
def wrapper():
return fun.upper()
return wrapper()
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())
Following problems occurs:
Traceback (most recent call last):
File "C:Python_projectsmain.py", line 17, in <module>
def function():
File "C:Python_projectsmain.py", line 13, in decorator2
return wrapper()
File "C:Python_projectsmain.py", line 11, in wrapper
return fun.upper()
AttributeError: 'function' object has no attribute 'upper'
or when I switch the order of decorators then it goes like this:
Traceback (most recent call last):
File "C:Python_projectsmain.py", line 17, in <module>
def function():
File "C:Python_projectsmain.py", line 6, in decorator1
return wrapper()
File "C:Python_projectsmain.py", line 4, in wrapper
return text + 'n' + fun + 'n' + text
TypeError: can only concatenate str (not "function") to str
When I run the code in this way, then it works just fine:
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun + 'n' + text
return wrapper()
def decorator2(fun):
def wrapper():
return fun.upper()
return wrapper()
def function():
return "Hey ya!"
print(decorator(decorator2(function())))
But it seems like using @ with decorators is much more popular. Do you have any idea what I am doing wrong?
First of all, a decorator is a function that accepts a function and returns a function. Thus, do not call the wrapper
, but return it. Moreover, the fun
method should be called.
def decorator1(fun):
def wrapper():
text = "------"
return text + "n" + fun() + "n" + text
return wrapper
def decorator2(fun):
def wrapper():
return fun().upper()
return wrapper
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())
Note that those decorators will work only with functions (or callables in general) that return str
and accept no arguments. Additionally, you can annotate the fun
argument properly with Callable[[], str]
:
from typing import Callable
def decorator1(fun: Callable[[], str]):
...
A decorator is a function which is run on another function, as in a function object, not the result of another function.
So:
@decorator
def func():
...
Is equivalent to:
def func():
...
func = decorator(func)
Notice that the function func
has never actually been called.
Therefore, within the wrapper function within the decorator, you actually need to call the function yourself. Use, for example, fun().upper()
, not fun.upper()
.
Also, the wrapper function should never be called within the decorator; the function itself should be returned, so return wrapper
not return wrapper()
.
Here is the corrected code:
def decorator1(fun):
def wrapper():
text = '------'
return text + 'n' + fun() + 'n' + text
return wrapper
def decorator2(fun):
def wrapper():
return fun().upper()
return wrapper
@decorator1
@decorator2
def function():
return "Hey ya!"
print(function())