def decorator(func):
def returning():
var = 1
func()
print(var)
return(returning)
@decorator
def function():
nonlocal var
var = 5
function()
var在调用func()之前在returns()函数内声明,但是我得到了一个绑定错误。
我不明白为什么会这样。
答案 0 :(得分:2)
Python在编译时确定范围 ,制作scope model static, not dynamic。 nonlocal
和global
语句告诉编译器更改设置名称的范围。 nonlocal
告诉编译器将给定名称作为闭包分配给生成封闭范围的闭包。请参阅Python执行模型文档的Naming and binding section:
如果名称绑定在块中,则它是该块的局部变量,除非声明为
nonlocal
或global
。
和
每个赋值或导入语句都出现在由类或函数定义或模块级别(顶级代码块)定义的块中。
和
范围定义块中名称的可见性。如果在块中定义了局部变量,则其范围包括该块。如果定义发生在功能块中,则范围将扩展到定义块中包含的任何块,除非包含的块为名称引入了不同的绑定。
因此,只有函数 definitions ,类定义和模块级别才是进行赋值的块。 nonlocal
只能对嵌套作用域中的名称起作用:
nonlocal
语句使相应的名称引用最近的封闭函数范围中的先前绑定的变量。
function()
不是嵌套块,没有封闭的函数范围。
装饰器是运行时功能,不会产生新的封装功能范围。你没有在装饰器中嵌套function()
,你只传递了对函数对象的引用。该函数已经创建,编译完成,名称的范围也是一成不变的。
执行所需操作的唯一方法是需要重新编译或字节码操作,这两个主题都是非常高级的Python黑客攻击。
例如,通过访问源代码(通常是这种情况),您可以使用inspect
和ast
将function
的抽象语法树合并到{{1}的抽象语法树中创建一个嵌套的作用域,然后将该新树编译成可以满足你想要的字节码。或者您必须使用两个函数的字节码执行类似操作,以使returning
生成闭包,并returning
将该闭包作为function()
的值。这需要深入了解Python闭包的工作原理,以及编译器为处理闭包所产生的字节码。
所有这些意味着找到一个与您的问题不同的方法会容易得多。也许使用具有状态的类来改变不同上下文中的状态等。