对装饰器中声明的非局部变量没有绑定

时间:2018-04-26 20:40:05

标签: python

def decorator(func):
    def returning():
        var = 1
        func()
        print(var)
    return(returning)
@decorator
def function():
    nonlocal var
    var = 5
function()

var在调用func()之前在returns()函数内声明,但是我得到了一个绑定错误。

我不明白为什么会这样。

1 个答案:

答案 0 :(得分:2)

Python在编译时确定范围 ,制作scope model static, not dynamicnonlocalglobal语句告诉编译器更改设置名称的范围。 nonlocal告诉编译器将给定名称作为闭包分配给生成封闭范围的闭包。请参阅Python执行模型文档的Naming and binding section

  

如果名称绑定在块中,则它是该块的局部变量,除非声明为nonlocalglobal

  

每个赋值或导入语句都出现在由类或函数定义或模块级别(顶级代码块)定义的块中。

  

范围定义块中名称的可见性。如果在块中定义了局部变量,则其范围包括该块。如果定义发生在功能块中,则范围将扩展到定义块中包含的任何块,除非包含的块为名称引入了不同的绑定。

因此,只有函数 definitions ,类定义和模块级别才是进行赋值的块。 nonlocal只能对嵌套作用域中的名称起作用:

  

nonlocal语句使相应的名称引用最近的封闭函数范围中的先前绑定的变量。

function()不是嵌套块,没有封闭的函数范围。

装饰器是运行时功能,不会产生新的封装功能范围。你没有在装饰器中嵌套function(),你只传递了对函数对象的引用。该函数已经创建,编译完成,名称的范围也是一成不变的。

执行所需操作的唯一方法是需要重新编译字节码操作,这两个主题都是非常高级的Python黑客攻击。

例如,通过访问源代码(通常是这种情况),您可以使用inspectastfunction的抽象语法树合并到{{1}的抽象语法树中创建一个嵌套的作用域,然后将该新树编译成可以满足你想要的字节码。或者您必须使用两个函数的字节码执行类似操作,以使returning生成闭包,并returning将该闭包作为function()的值。这需要深入了解Python闭包的工作原理,以及编译器为处理闭包所产生的字节码。

所有这些意味着找到一个与您的问题不同的方法会容易得多。也许使用具有状态的类来改变不同上下文中的状态等。

相关问题