嵌套作用域中变量绑定的精确规则
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-级别范围)。
我似乎对 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-级别范围)。