【问题标题】:Function Attributes - Scope函数属性 - 范围
【发布时间】:2017-04-15 23:14:19
【问题描述】:

所以我有这个代码:

def collect_input(func):
    """
    A decorator which adds an all_input attribute to the wrapped function.
    This attribute collects any input passed to the function.
    """
    def wrapper(*args, **kwargs):
        wrapper.all_input.append(*args)
        return func(*args, **kwargs)

    wrapper.all_input = []
    return wrapper

@collect_input
def foo(bar):
    print('in foo')

foo(5)
foo('spam')

print(foo.all_input)

我的问题是:如果foo.all_input 是在collect_input 范围内声明的,为什么你可以访问它?

【问题讨论】:

  • 实际上,你访问的不是foo.all_input,而是wrapper.all_input,因为你已经用一个返回wrapper函数的装饰器包装了你的函数。

标签: python function scope attributes decorator


【解决方案1】:

Python 的好处是它有一套规则来处理它的对象,而且这些规则很少有例外。

在这种情况下,被修饰的函数只是一个普通的 Python 对象。一个碰巧也是可调用的。

collect_input 内的wrapper.all_input = [] 行中发生的情况是,它在对象上设置了一个属性——此时名为wrapper——但该对象将被返回并取代foo 函数在全球范围内。这就是装饰器的工作方式。

所以让我们一步一步来,让它尽可能清晰:

  1. 在运行上面的代码时,它定义了collect_input 函数——该函数旨在用作装饰器。

  2. 然后它定义了foo函数,但是在添加到全局作用域之前,它被传递到collect_input函数中。这就是“@”语法的作用。在它存在之前,装饰函数的方式是首先定义一个函数,然后用普通赋值替换装饰器的返回值。所以,上面的代码是一样的:

    def foo(...): ...

    foo = collect_input(foo)

  3. 在“collect_input”内部,原始的foo 函数将在新的wrapper 函数内部调用。这个wrapper 函数:每次调用装饰器collect_input 时都会创建一个新的(函数)对象,该对象将取代最外层的foo 定义。您可以看到,在 wrapper 的代码中,有额外的代码来完成 collect_input 的用途:在列表中注释输入参数,附加到自身,然后恢复对原始函数的调用 - @987654334 @ 在这种情况下。

  4. 最后,collect_input 返回的wrapper 对象取代了foo,但在装饰器调用中附加了all_inputs 列表。因此,它可以作为foo 对象的属性在全局范围内访问——不管它是在哪里定义的。请注意,您不能在函数之外使用名称 funcwrapper,如预期的那样。

【讨论】:

    【解决方案2】:
    @collect_input
    def foo(bar):
        print(foo.__name__) # wrapper
        print('in foo')
    

    你可以看到wrapper如何取代foo

    【讨论】:

      猜你喜欢
      • 2017-11-13
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多