嵌套作用域中变量绑定的精确规则

Precise rules of variable binding in nested scopes

我似乎对 Python 变量绑定有一些误解。给定带有阴影名称的嵌套范围,决定访问哪个变量的精确规则是什么?

让我用一些例子来说明。首先是基本阴影。

a = 1

def foo():
    a = 2
    def _foo():
        return a
    return _foo()

print(foo())  # -> 2

这里一切都很好。该值被覆盖并相应地返回。但是,如果在函数定义后改变了值,它仍然是内部值:

def bar():
    def _bar():
        return a
    a = 2
    return _bar()

print(bar())  # -> 2

更重要的是,定义一个引用不存在的变量的函数是可能的。

def baz():
    def _baz():
        return b
    return _baz()

那么,如果后面定义了b,函数就可以执行了。 但是 not if 是在另一个内部范围内定义的:

def qux(f):
    b = 3
    return f()

print(qux(baz()))  # -> NameError

现在所有这些情况都可以通过让 Python 了解程序后面的行来解释,但这与我对 Python 是一种解释性语言的认识相冲突,将行推进线。那么语句是一次解析而不是逐行解析的吗?

带有阴影 class 属性的奇怪行为让我更加反感。

class C:
    a = 2
    b = a

    def meth(self):
        return a

    c = meth

print(C.b, C().meth(), C.c)  # -> 2 1 C.meth

此处 a 被定义为 class 属性并在 b 中成功使用,但这不会延续到方法定义中。该方法本身可以在以后的属性中使用,但不能在其他方法中使用,例如不经过 self.

我对绑定同时发生的猜测是否正确?在那种情况下,class 主体是设计上的例外,还是它们根本不是一个范围?还是这里发生了其他事情?

我想你可能想多了。

默认情况下,创建的变量会放在最窄的封闭函数范围内。

来自所有 封闭范围的变量在只读容量 中可用,无论是封闭函数的范围还是全局范围。如果您尝试分配给它,它会在最窄的封闭范围内创建一个新变量,遮蔽外面的变量。使用 global 关键字将外部变量引入本地范围将阻止这种情况发生,允许您将事物分配给非本地范围。

此外,请记住,函数是在解释 def 语句时编译和求值的。对于嵌套函数,本质上,每次新调用都会重新计算内部函数。这也意味着内部函数对外部函数的范围具有只读访问权限。和往常一样的规则。

您的 bar() 示例之所以有效,是因为当 python 尝试访问变量 a 时,它至少存在于一个封闭范围中。 Python 直到最后一刻才检查这些东西。您的 qux() 示例不起作用,因为声明 b 的范围 不包含 定义 _baz() 的范围,因此无法访问。


Class 范围很奇怪。当计算 class 时,其中定义的所有变量都绑定到 class。但是,就其中包含的方法而言,class 并不真正算作它自己的范围。将 meth() 视为在全局范围内声明的未绑定函数,C.meth 指的是(现在是 C.c)。通过点符号调用函数是一种语法 shorthand:

# the following two are identical
C().meth()
C.meth(C())

虽然 C.meth 在技术上绑定到 C,但它并未包含在 C 的 class 级命名空间中。尝试执行 C().meth() 将失败,因为 a 未针对函数定义。 (请注意,如果 a 是在全局范围内定义的,该函数将按预期工作 - C.meth() 具有作为父级的全局范围,而不是 C 的 class-级别范围)。