python - Python中类和函数范围之间的行为差异
问题描述
您可能知道,变量的范围在 python 中是静态确定的(Python中局部变量和全局变量的规则是什么?)。
例如 :
a = "global"
def function():
print(a)
a = "local"
function()
# UnboundLocalError: local variable 'a' referenced before assignment
相同的规则适用于类,但它似乎默认为全局范围而不是引发AttributeError
:
a = "global"
class C():
print(a)
a = "local"
# 'global'
此外,在嵌套函数的情况下,行为是相同的(不使用nonlocal
or global
):
a = "global"
def outer_func():
a = "outer"
def inner_func():
print(a)
a = "local"
inner_func()
outer_func()
# UnboundLocalError: local variable 'a' referenced before assignment
但是在嵌套类的情况下,它仍然默认为全局范围,而不是外部范围(同样不使用global
or nonlocal
):
a = "global"
def outer_func():
a = "outer"
class InnerClass:
print(a)
a = "local"
outer_func()
# 'global'
最奇怪的部分是嵌套类在没有声明时默认为外部范围a
:
a = "global"
def outer_func():
a = "outer"
class InnerClass:
print(a)
outer_func()
# 'outer'
所以我的问题是:
- 为什么函数和类之间存在差异(一个引发异常,另一个默认为全局范围。
- 在嵌套类中,为什么在使用之后定义的变量时,默认范围必须变为全局而不是继续使用外部范围?
解决方案
官方文档的第 9.2 节中给出了非常详细的答案。问题的关键是
...另一方面,名称的实际搜索是在运行时动态完成的——但是,语言定义正在向静态名称解析发展,在“编译”时,所以不要依赖动态名称解析!(实际上,局部变量已经是静态确定的。)
当您在类定义中时,在其执行时刻是最内部的范围,动态名称解析适用。因此,您会看到 的全局值的打印输出a
。
如果名称解析是静态的,就像在函数定义中一样,a
即使在 print 语句中,该名称也会被识别为本地名称。这就是为什么你不能a
在分配给它之前打印一个函数。
第 4.2.2 节提到了类主体范围的规则:
类定义块和参数在名称解析的上下文中是特殊的
exec()
。eval()
类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则,但在全局命名空间中查找未绑定的局部变量除外。
让我们仔细解析最后一句,因为它完全涵盖了你的最后两个示例。首先,在这种情况下,什么是未绑定的局部变量?类体创建一个新的命名空间,就像输入一个函数一样。如果名称绑定在类主体的某处,则它是局部变量。如上所述,这是静态确定的。如果您尝试在首次绑定之前引用该名称,则您有一个未绑定的局部变量。python 不会像函数调用那样引发错误,而是直接跳转到全局命名空间来执行查找(并且也忽略内置函数)。在所有其他情况下(不是局部变量),正常的 LEGB 查找顺序适用。
这确实有点违反直觉,我认为它推动了,如果不是彻底打破了最不意外的规则。
推荐阅读
- python - 将自定义函数应用于 pandas groupby 对象
- java - Java OpenCSV 如何使用 HeaderColumnNameTranslateMappingStrategy 捕获异常/错误
- java - BufferedImage 的 JAVA NullPointerException
- xcode - 无法安装 - 找不到此可执行文件的有效配置文件 Xcode 11
- java - ANDROID STUDIO 本地 json 文件在更改活动时重置
- hy - walk 必须返回与输入相同的类型?
- javascript - 一旦不再可点击,如何使下一个按钮消失?
- javascript - 如何使用 JQuery 更改 EJS 中的单个元素
- python - pandas:如何通过选择列范围来过滤行?
- ios - 如何有效地绘制可以平移和缩放的无限网格?