修饰符:理解它为什么不刷新局部变量
问题描述
我写了一个简单的装饰符:
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
...
为什么?我以为decorator
只是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
时,我们将看到它们各自累积了自己的(不同的)随机数列表。换句话说,每次将您的修饰符应用于某个对象时,都会创建一个新的列表,并且每次调用"某个对象"时,该列表都会增长。
相关文章