python - 装饰器:理解为什么它不刷新局部变量

标签 python python-3.x decorator python-decorators

我写了一个简单的装饰器:

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")

现在,如果我多次调用 foolst 不会被刷新。相反,它会随着时间的推移而积累。因此,多次调用 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/

相关文章:

python - a.insert(0,x) 是一个 o(n) 函数吗? a.append 是一个 O(1) 函数吗? Python

javascript - 如何动态生成 Blockly 下拉菜单的内容?

python - 多维 numpy 数组——沿给定轴反转

在 virtualenv 中全局安装 Python3 pip

python - 按值列表的平均值对字典进行排序

Python 修饰类中的方法和继承

python - 我的 IDLE 无法将 itertools.izip() 识别为函数

python - 在 python 中查找 tan 逆

c# - Autofac 通用装饰器正在复制组件

python - 对类方法使用 pydispatch 和装饰器