Python中的nonlocal依赖于层级吗?
这个问题是关于Python变量作用域的后续讨论。还有其他相关问题q1、q2和一些答案,可以在StackOverflow上找到,内容还有很多。
官方的Python文档和PEP 3104应该能解释这些细节,但我觉得它们并没有完全自解释。
我想解决的问题是重构包含nonlocal
/global
的代码,通过将这些代码上下移动一个层级。
我不明白的是,Python参考文档中的这句话有什么含义:
在
nonlocal
语句中列出的名称,与在global
语句中列出的名称不同,必须指向在封闭作用域中已经存在的绑定(无法明确确定新绑定应该创建在哪个作用域中)。
考虑以下在全局作用域中的代码:
var = 0
def outer():
global var # line A
var = 1
def inner():
nonlocal var # line B
var = 2
print('inner:', var)
inner()
print('outer:', var)
outer()
print('main:', var)
执行时会抛出错误:
SyntaxError: no binding for nonlocal 'var' found
如果注释掉A行,代码可以正常工作(当然,语义会有所不同):
inner: 2
outer: 2
main: 0
或者如果注释掉B行:
inner: 2
outer: 1
main: 1
但是,在上面的例子中,由于nonlocal
应该将var绑定到“封闭作用域”,我本来以为A行会将外部的var绑定到全局作用域,然后B行再查找外部的var,并将内部的var重新绑定到全局的var。结果似乎根本找不到(我想是因为A行的重新绑定),所以抛出了错误。
我期望的结果是:
inner: 2
outer: 2
main: 2
这只是Python作用域混乱状态的又一个例子吗?
或者,为了让这个问题更具建设性:
- 如何编写这样的例子,使得函数所在的层级无关紧要(可以在
global
和nonlocal
之间互换)? - 如果函数位于一个中间且未知的层级,
outer()
的作者如何修改代码,使得既不触及最外层(在这种情况下是全局层),也不触及inner()
层?
根据我对这门语言的理解,这种构造(依赖于闭包)应该尽量避免。其他人已经建议使用其他语言特性(类、函数属性)来实现这种上下文敏感性。
2 个回答
怎么写一个例子,让函数的位置不重要(也就是说,不用在全局和非局部之间来回切换)?
函数的位置其实不重要,重要的是变量的位置。
如果函数处在一个中间层次,而且这个层次是未知的,outer()的作者怎么能修改代码,让最外层(在这个例子中是全局层)和内层(inner())都不需要改动呢?
你在问,是否有可能让一个中间层的函数通过修改代码,影响到内层函数的变量,从而改变全局范围内的东西或者外层函数的局部范围。这听起来确实有点奇怪。
global
和 nonlocal
这两个词是不能一起用的,因为它们的意思不一样:
global
表示这个名字是在模块的顶层定义的nonlocal
表示这个名字是在外层函数的作用域里
你遇到的错误是因为你告诉 Python var
是非本地的(也就是它在外层函数里),但是在任何外层函数里并没有为 var
绑定一个函数级别的定义,因为你在外层函数里告诉 Python var
是全局的。