Why can wrapper() call txt but italic() cannot (when using it as a decorator)?
Helpful if you can explain the order things get executed, and how the call to fun within the wrapper is different than calling in italic as a decorator (example 1,2) and not (example 3). Thanks!
# EXAMPLE 1: call function with wrapper
def italic(fun):
def wrapper():
return '<i>' fun() '</i>'
return wrapper
@italic
def txt():
return 'merp'
print(txt())
Output:
<i>merp</i>
# EXAMPLE 2: call function directly
def italic(fun):
return '<i>' fun() '</i>'
@italic
def txt():
return 'merp'
print(txt())
Output:
TypeError: 'str' object is not callable
Why does it see txt as a string here? But if I try return '<i>' fun '</i>'
, it says cannot concatenate function to string lol
edit: nvm, the type error and uncallable errors were from two different lines; i got them confused ><
# EXAMPLE 3: nest functions instead of using as decorator
def italic(fun):
return '<i>' fun() '</i>'
def txt():
return 'merp'
print(italic(txt))
Output:
<i>merp</i>
CodePudding user response:
@italic
def func():
...
is syntax sugar for:
func = italic(func)
Therefore you can understand what happens here, the decorator is executed at decoration time. It should return another callable, because the result of calling the decorator replaces the function which it decorates. If you do it like in example 2/3 then it is replacing the function object txt
with a string object txt
, and the function object is now shadowed (unavailable in the namespace):
>>> def italic(fun):
... return '<i>' fun() '</i>'
...
>>> @italic
... def txt():
... return 'merp'
...
>>> txt # this is now a string, not a function. you can't call it.
'<i>merp</i>'
If you want to work with a "closure-less" style decorator like in example 2/3, there is a popular third-party library called decorator which can help with that:
>>> from decorator import decorator # pip install decorator
>>> @decorator
... def italic(fun):
... return '<i>' fun() '</i>'
...
>>> @italic
... def txt():
... return 'merp'
...
>>> txt()
'<i>merp</i>'