decorator:理解它为什么不刷新局部变量

2024-06-28 15:41:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我写了一个简单的装饰:

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。你知道吗


Tags: fromimportreturnfoomydefargs装饰
1条回答
网友
1楼 · 发布于 2024-06-28 15:41:39

你说得对。。。装潢师只是一个语法糖。具体来说:

@decorator
def foo():
    pass

与以下内容完全相同:

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

让我们更离奇一点,用另一种方式重写它,大体上相当于

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

希望这样写出来,你可以看到,如果我多次调用foo,我不是在调用decoratordecorator只被调用一次(以帮助创建foo)。你知道吗

现在在您的示例中,您的decorator在被调用时立即创建一个列表:

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(取决于decorator所做的工作,您可能会看到函数的__name__属性存在一些差异,但在其他情况下是相同的…


还要注意,每次调用decorator时都有一个lst。如果我们喜欢的话,我们可以疯狂地用这个来装饰foo两次:

@my_dec
@my_dec
def foo():
    pass

或者我们可以装饰多个功能:

@my_dec
def foo():
    pass

@my_dec
def bar():
    pass

当我们调用foobar时,我们会看到它们各自积累了自己的(不同的)随机数列表。换句话说,每当你的装饰器被应用到某个东西上时,就会创建一个新的列表,并且每次调用“某物”时,列表就会增长。你知道吗

相关问题 更多 >