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

时间:2016-12-07 07:34:24

标签: 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")

现在,如果我多次呼叫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

1 个答案:

答案 0 :(得分:3)

你是对的...装饰者只是语法糖。具体做法是:

@decorator
def foo():
    pass

完全相同:

def foo():
    pass
foo = decorator(foo)

让我们更加古怪,并以另一种方式改写 等同于 1

def bar():
    pass
foo = decorator(bar)
del bar

希望以这种方式写出来,你可以看到,如果我多次拨打foo,我多次呼叫decoratordecorator只被调用一次(以帮助创建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

然后,当我们拨打foobar时,我们会看到他们每个人都积累了自己的(不同的)随机数列表。换句话说,每次将装饰器应用于某个东西时,都会创建一个新列表,并且每次都会创建一个"某些东西"被叫,名单会增长。