I know that the decorator is a function that takes another function and extends its behavior. In the example below, I previously assume that test() now is effectively equivalent to decorator(test)().
def decorator(func):
def wrapper(*args, **kwargs):
...
res = func(*args, **kwargs)
...
return res
return wrapper
@decorator
def test():
pass
However, after adding a function attribute in the decorator and run both test() and decorator(test)(), the results are different. It seems that in the case of decorator(test)(), the decorator function is indeed ran so that num is reset; when using @decorator instead, the decorator function is not ran as I expected?
def decorator(func):
decorator.num = 0
def wrapper(*args, **kwargs):
...
res = func(*args, **kwargs)
...
return res
return wrapper
@decorator
def test():
pass
def test2():
pass
decorator.num = 5
test()
print(decorator.num)
decorator.num = 5
decorator(test2)()
print(decorator.num)
---------------------
5
0
Your confusion stems from when the decorator runs. The syntax
@decorator
def foo(): ...
is equivalent to
def foo(): ...
foo = decorator(foo)
That is, immediately after the function is defined, the decorator is called on it, and the result of calling the decorator is assigned back to the original function name. It's called only once, at definition time, not once per function call.
The same is true of classes. The syntax
@decorator
class Foo: ...
is equivalent to
class Foo: ...
Foo = decorator(Foo)