我写了一个简单的装饰器:
from functools import wraps
import random
def my_dec(f):
lst = list()
@wraps(f)
def wrapper(*args):
lst.append(random.randint(0, 9))
print(lst)
return f(*args)
return wrapper
@my_dec
def foo():
print("foo called")
现在,如果我多次调用 foo
,lst
不会被刷新。相反,它会随着时间的推移而积累。因此,多次调用 foo 会返回如下输出:
foo()
> [4]
> foo called
foo()
> [4, 9]
> foo called
foo()
> [4, 9, 1]
> foo called
...
这是为什么呢?我以为装饰器只是 my_dec(foo) 的语法糖?!我假设每次调用 my_dec
都会刷新 lst
。
最佳答案
你是对的......装饰器只是语法糖。具体来说:
@decorator
def foo():
pass
与以下内容完全相同:
def foo():
pass
foo = decorator(foo)
让我们更古怪一点,用另一种大部分等效的方式重写此1:
def bar():
pass
foo = decorator(bar)
del bar
希望这样写,你可以看到如果我打电话 foo
很多次,我没有调用decorator
好几次了。 decorator
仅被调用一次(以帮助创建 foo
)。
现在在您的示例中,您的装饰器在被调用时立即创建一个列表:
def my_dec(f):
lst = list() # list created here!
@wraps(f)
def wrapper(*args):
lst.append(random.randint(0, 9))
print(lst)
return f(*args)
return wrapper
函数返回wrapper
被分配给您的foo
,所以当您调用foo
时,您正在调用wrapper
。注意wrapper
中没有代码这将重置 lst
-- 仅向 lst
添加更多元素的代码所以这里没有任何内容来指示 lst
应该在调用之间“刷新”。
1(根据装饰器的作用,您可能会在函数的 __name__
属性中看到一些差异,但在其他方面都是相同的...)
另请注意,您将拥有一个 lst
每次调用装饰器时。如果我们喜欢并装饰的话,我们可以对这个感到疯狂foo
两次:
@my_dec
@my_dec
def foo():
pass
或者我们可以装饰多个函数:
@my_dec
def foo():
pass
@my_dec
def bar():
pass
然后当我们调用foo
时和bar
,我们会看到它们每个都积累了自己的(不同的)随机数列表。换句话说,每次将装饰器应用于某物时,都会创建一个新列表,并且每次调用该“某物”时,该列表都会增长。
关于python - 装饰器:理解为什么它不刷新局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41011744/