Python中的nonlocal依赖于层级吗?

11 投票
2 回答
14287 浏览
提问于 2025-04-17 05:51

这个问题是关于Python变量作用域的后续讨论。还有其他相关问题q1q2和一些答案,可以在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作用域混乱状态的又一个例子吗?

或者,为了让这个问题更具建设性:

  • 如何编写这样的例子,使得函数所在的层级无关紧要(可以在globalnonlocal之间互换)?
  • 如果函数位于一个中间且未知的层级,outer()的作者如何修改代码,使得既不触及最外层(在这种情况下是全局层),也不触及inner()层?

根据我对这门语言的理解,这种构造(依赖于闭包)应该尽量避免。其他人已经建议使用其他语言特性(函数属性)来实现这种上下文敏感性。

2 个回答

0

怎么写一个例子,让函数的位置不重要(也就是说,不用在全局和非局部之间来回切换)?

函数的位置其实不重要,重要的是变量的位置。

如果函数处在一个中间层次,而且这个层次是未知的,outer()的作者怎么能修改代码,让最外层(在这个例子中是全局层)和内层(inner())都不需要改动呢?

你在问,是否有可能让一个中间层的函数通过修改代码,影响到内层函数的变量,从而改变全局范围内的东西或者外层函数的局部范围。这听起来确实有点奇怪。

21

globalnonlocal 这两个词是不能一起用的,因为它们的意思不一样:

  • global 表示这个名字是在模块的顶层定义的
  • nonlocal 表示这个名字是在外层函数的作用域里

你遇到的错误是因为你告诉 Python var 是非本地的(也就是它在外层函数里),但是在任何外层函数里并没有为 var 绑定一个函数级别的定义,因为你在外层函数里告诉 Python var 是全局的。

撰写回答