【问题标题】:decorator: understanding why it does not flush local variables装饰器:理解为什么它不刷新局部变量
【发布时间】:2017-04-22 01:25:02
【问题描述】:

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

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

...

这是为什么呢?我认为decorator 只是my_dec(foo) 的语法糖?!我假设每次调用my_dec 都会刷新lst

【问题讨论】:

  • 你说得对,装饰器只是foo = my_dec(foo) 的语法糖......但我不明白这会如何让你认为lst 会被“冲洗”。这里要小心——my_dec 只会被调用一次,无论你调用多少次foo...
  • 哦,是的,你是对的。我认为每次调用foo 时都会调用my_dec,但显然不是,因为foo = my_dec(foo) 只发生一次。感谢您的澄清!
  • 附加问题:我可以通过某种方式访问​​lst吗?

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


【解决方案1】:

你是对的......装饰器只是语法糖。具体来说:

@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 时,我们会看到它们各自积累了自己的(不同的)随机数列表。换句话说,每次你的装饰器被应用到某个东西上时,都会创建一个新的列表,并且每次调用这个“某物”时,这个列表都会增长。

【讨论】:

  • 说得好 - 很好的解释!附加问题:每个装饰函数foo_n 都有自己的lst,对吗?因此,我可以这样记住东西吗?有什么方法可以访问lst
  • @ascenator -- 我只是在更新我的编辑,说每个人都有自己的lst。没有 clean 方法可以访问该列表。您可以在返回之前将其作为属性添加到函数 (wrapper)...wrapper.lst = lst; return wrapper,但这开始感觉有点笨拙
  • 你的意思是用装饰器来保存和加载值不是 pythonic 吗?或者通常没有干净的方法来使用装饰器存储特定数据(并稍后阅读)?我突然想到了,因为以这种方式存储值对我来说听起来是个好主意。否则每次调用foo_n 时都必须手动执行此操作。
  • 我还没有具体的用例。例如,我只是想,可以分析或至少计算函数调用。或者为需要大量计算时间的函数构建自己的缓存查找,而它们的返回值很容易放入内存中。这样,甚至可以查看到目前为止计算的所有值。或者实现缓冲区,...简而言之:我只是在考虑存储信息并稍后访问它的用例。
  • @ascenator 是的,您可以为此使用装饰器。看看memoization decoratorsnumber of function calls decorators。是否应该取决于您的用例。
猜你喜欢
  • 1970-01-01
  • 2018-05-01
  • 2017-03-01
  • 1970-01-01
  • 2021-05-10
  • 2011-10-30
  • 1970-01-01
  • 2020-01-31
  • 1970-01-01
相关资源
最近更新 更多