Python 函数属性-范围

Python 函数属性-范围,python,function,scope,attributes,decorator,Python,Function,Scope,Attributes,Decorator,所以我有这个代码: 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.ap

所以我有这个代码:

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)

我的问题是:如果在
collect\u input
范围中声明了
foo.all\u input
,那么为什么您可以访问它呢?

Python的优点是它有一组规则来处理它的对象,并且这些规则很少有例外

在本例中,修饰的函数只是一个普通的Python对象。一个碰巧也是可以调用的

collect\u input中的
wrapper.all\u input=[]
行中发生的情况是,它在对象上设置了一个属性,在该点上命名为
wrapper
,但该属性是将返回的对象,并在全局范围内取代
foo
函数。这就是装饰师的工作方式

因此,让我们一步一步地把它弄清楚:

  • 当运行上述代码时,它定义了
    collect\u input
    函数,该函数被设计为用作装饰器

  • 然后它定义了
    foo
    函数,但在将其添加到全局范围之前,它被传递到
    collect\u input
    函数中。这就是“@”语法的作用。在函数存在之前,修饰函数的方法是首先定义一个函数,然后用普通赋值替换它作为修饰器的返回值。因此,上述代码与:

    def foo(…):

    foo=收集输入(foo)

  • 在“collect_input”中,原始的
    foo
    func将在新的
    wrapper
    函数中调用。这个
    wrapper
    函数:每次调用decorator
    collect\u input
    时创建的新(函数)对象将取代最外层的
    foo
    定义。您可以看到,在
    wrapper
    的代码中有额外的代码来完成
    collect\u input
    的目的:在一个列表中注释输入参数,并附加到其自身,然后继续调用原始函数-
    foo

  • 最后,
    collect\u input
    返回的
    wrapper
    对象取代了
    foo
    ,但在decorator调用中附加了
    all\u input
    列表。因此,它可以作为
    foo
    对象的属性在全局范围内访问,而不管它是在哪里定义的。请注意,在函数之外,不能按预期使用名称
    func
    wrapper


  • 您可以看到
    wrapper
    如何取代
    foo

    实际上,您并不是在访问
    foo.all\u input
    而是
    wrapper.all\u input
    ,因为您使用了一个返回
    wrapper
    函数的装饰程序来包装函数。
    @collect_input
    def foo(bar):
        print(foo.__name__) # wrapper
        print('in foo')