Python的非本地取决于层次结构?

Posted

技术标签:

【中文标题】Python的非本地取决于层次结构?【英文标题】:Python's nonlocal depends on level of hierarchy? 【发布时间】:2011-12-24 10:19:28 【问题描述】:

这个问题是a question about Python variable scope 的后续问题。更多问题q1、q2 和answers 可以在 SO 上找到,甚至更多。 官方Python documentation 和PEP 3104 应该会解释细节,但对我来说,它们似乎并不完全不言自明。

我要解决的主题是重构包含nonlocal/global 的代码,方法是将代码向上/向下移动一级层次结构。

我不明白的是 Python 参考中这句话的含义:

在非本地语句中列出的名称,与在非本地语句中列出的名称不同 全局语句,必须引用封闭中的预先存在的绑定 范围(不能在其中创建新绑定的范围 明确地确定)。

鉴于以下关于全局范围的代码:

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 并将 inner/var 重新绑定到 global/var。相反,它似乎根本找不到它(我想是由于 A 行中的重新绑定)并引发了错误。

我期望的结果是:

inner: 2
outer: 2
main: 2

这只是 Python 中范围界定令人困惑的另一个例子吗?

或者,让这个问题成为一个建设性的问题:

如何编写这样的示例,使其与函数驻留在哪个级别无关(必须将globalnonlocal 交换,反之亦然)? 如果函数位于一个中间的、未知的层次结构中,outer() 的作者如何更改既不需要触及最外层(在本例中为全局)也不需要触及inner() 层的代码? -

以我对这门语言的粗浅理解,应该避免像这样的结构(依赖于闭包)。其他人已经建议使用其他语言功能(classes、func attrs)来实现这种上下文敏感性。

【问题讨论】:

全局变量和局部变量在 Python 中是根本不同的。 nonlocal 语句只能引用封闭范围的局部变量,而不能引用全局变量——毕竟这就是 global 的用途。 globalnonlocal 背后的概念相当不同。您对当前状态如何构成问题的论点完全是构建的。我的回答是:不要使用全局变量,不要使用global,所有提到的“问题”(以及更多)都会消失。 虽然我通常同意避免使用全局变量,但其他人似乎发现这个概念非常有用,以至于 Python 提供了 global 关键字,Py3k 甚至添加了 nonlocal。我正在尝试了解它们组合使用的含义。 "……Py3k 甚至添加了一个nonlocal。" nonlocal 旨在使闭包更有用。它与全局变量无关。 【参考方案1】:

globalnonlocal 不适合组合使用。它们的含义不同:

global 表示名称存在于模块级别 nonlocal 表示名称存在于外部词法函数作用域中

您得到原始异常的原因是因为您告诉 Python var 是非本地的(意味着它在外部函数定义中),但是对于 @ 没有 没有函数级绑定 987654326@ 在任何外部函数定义中,因为您在外部函数中告诉 Python var 是全局的。

【讨论】:

“global 和 nonlocal 不应该结合”,这正是我的问题:如果不是,那么代码的重构就会变得更加困难。假设最里面的函数使用nonlocal 和直接的周围函数。如果周围的函数将绑定更改为global,那么最里面的函数也必须更改。但是,唉,手头的语法我看不到另一种方式。 (但要使用类包装器等) 我明白你的意思,但如果你将闭包重构为非闭包,我认为这不是不合理的工作量。【参考方案2】:

这样的例子怎么能写成一个无关紧要的 函数所在的级别(必须与全局交换 非本地的,反之亦然)?

函数驻留在哪个级别并不重要。只有变量位于哪个级别才重要。

如果函数位于中间且未知的级别 层次结构,outer() 的作者如何更改代码 既不是最外层(在这种情况下是全局),也不是 inner() 等级必须要摸?

您在问一个中间级别的函数是否有可能通过更改其代码中的某些内容来导致内部函数中的变量交替更改全局范围内的某些内容或外部函数的局部范围内的某些内容.这似乎是一件非常奇怪的事情。

【讨论】:

我的观点是关于重构:如果中间函数及其内部函数向下移动一级,则必须在 global 中更改 nonlocal,即使其直接周围的函数会也可以使用变量。嗯,我开始认为我选择了一个不好的例子来详细说明我在使用这种语法/范围时遇到的问题。 @cfi 我只是想确保我理解您的意思:“如果中间函数及其内部函数向下移动一级”。最初,varouter 都在另一个函数的块中,因此outerinner 都使用nonlocal var。通过将“中间函数及其内部函数向下移动一级”,您的意思是将varouter 都移近全局范围?如果varouter 的这个新作用域是全局作用域,那么您必须将outerinner 中的nonlocal var 都更改为global var

以上是关于Python的非本地取决于层次结构?的主要内容,如果未能解决你的问题,请参考以下文章

我可以为 YAML 层次结构中的非叶节点分配值吗?

如何使用python脚本从网站中废弃数据并以层次结构存储在文件夹中?

LaTex实战笔记 2-文档层次与结构

IfcSpatialElement

存储器层次结构中的缓存

python模块层次结构命名约定