Understanding decorators: return type is function when no argument is specified
I use one decorator for two separate functions: one with a decorator argument specification; and one more without it.
When no optional argument is passed, the return type is a function (specifically inner_function
in a decorator). However, when an optional argument is passed, it works as expected.
Can you explain what is happening here and why it works differently in these cases?
def cache_disk(cache_folder="./cache"):
def wrapper(f):
def inner_function(*args, **kwargs):
result = f(*args, **kwargs)
return result
return inner_function
return wrapper
@cache_disk
def func1(data):
return [d for d in data]
@cache_disk(cache_folder='./cache/')
def func2(data):
return [d for d in data]
data = [1,2,3]
print(func1(data))
print(func2(data))
Result:
<function inner_function at 0x7f1f283d5c08>
[1, 2, 3]
source to share
Note that:
@decorator # no arguments
def func(...):
...
equivalent to:
def func(...):
...
func = decorator(func) # one 'level' of calls
So what:
@decorator(...): # arguments
def func(...):
...
equivalent to:
def func(...):
...
func = decorator(...)(func) # two 'levels' of calls
In the first case, there is one argument for the decorator, itself func
. In the second case, the arguments to the decorator are ...
from a string @
, and this is the function returned by the decorator that is called with func
as an argument.
In your example
@cache_disk
def func1(data):
...
the decorator cache_disk
takes one argument called ( func
which becomes args[0]
) and returns wrapper
. Then when you call:
print(func1(data))
wrapper
takes a single argument ( data
which becomes f
) and returns inner_function
.
Hence, you have three options:
- Decorate
func1
with@cache_disk()
(mark parentheses) without passing arguments << 29> andfunc
onwrapper
; - Alter
cache_disk
behave differently depending on whether it passed one, a called argument, or something else; or - As @ o11c pointed out in the comments , use for example
cache_disk.wrapper = cache_disk()
to provide a convenient alias for the parameterless version, then decorate with@cache_disk.wrapper
.
source to share