Python中类和函数作用域之间的行为差异

Python中类和函数作用域之间的行为差异,python,python-3.x,Python,Python 3.x,您可能知道,变量的范围是在python()中静态确定的 例如: a=“全局” def函数(): 印刷品(a) a=“本地” 函数() #UnboundLocalError:赋值前引用的局部变量“a” 同样的规则也适用于类,但它似乎默认为全局范围,而不是引发AttributeError: a=“全局” C类(): 印刷品(a) a=“本地” #“全球” 此外,在嵌套函数的情况下,行为是相同的(不使用非局部或全局): a=“全局” def outer_func(): a=“外部” 定义内部函数(

您可能知道,变量的范围是在python()中静态确定的

例如:

a=“全局”
def函数():
印刷品(a)
a=“本地”
函数()
#UnboundLocalError:赋值前引用的局部变量“a”
同样的规则也适用于类,但它似乎默认为全局范围,而不是引发
AttributeError

a=“全局”
C类():
印刷品(a)
a=“本地”
#“全球”
此外,在嵌套函数的情况下,行为是相同的(不使用
非局部
全局
):

a=“全局”
def outer_func():
a=“外部”
定义内部函数():
印刷品(a)
a=“本地”
内函数()
外函数
#UnboundLocalError:赋值前引用的局部变量“a”
但是对于嵌套类,它仍然默认为全局范围,而不是外部范围(同样不使用
global
nonlocal
):

a=“全局”
def outer_func():
a=“外部”
类内部类:
印刷品(a)
a=“本地”
外函数
#“全球”
最奇怪的是,当没有声明
a
时,嵌套类默认为外部作用域:

a=“全局”
def outer_func():
a=“外部”
类内部类:
印刷品(a)
外函数
#“外部”
因此,我的问题是:

  • 为什么函数和类之间存在差异(一个引发异常,另一个默认为全局范围)
  • 在嵌套类中,当使用后来定义的变量时,为什么默认范围必须变为全局范围而不是保持使用外部范围

答案在官方文件中给出得非常详细。问题的关键是

…另一方面,对名称的实际搜索是在运行时动态完成的-但是,语言定义在“编译”时正朝着静态名称解析的方向发展,所以不要依赖于动态名称解析!(事实上,局部变量已经是静态确定的。)

当您在类定义中时(执行时是最内部的作用域),将应用动态名称解析。因此,您将看到
a
的全局值的打印输出

如果名称解析是静态的,如在函数定义中,则名称
a
即使在print语句中也会被识别为本地名称。这就是为什么在分配给函数之前不能在函数中打印
a

课程主体范围的规则在以下章节中有所提及:

类定义块和参数在名称解析上下文中是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则,但在全局命名空间中查找未绑定的局部变量除外

让我们仔细分析最后一句话,因为它完全涵盖了最后两个示例。首先,在这个上下文中,什么是未绑定的局部变量?类主体创建一个新的名称空间,就像输入一个函数一样。如果名称绑定在类主体的某个位置,它就是一个局部变量。这是静态确定的,如上所述。如果如果在首次绑定名称之前尝试引用该名称,则有一个未绑定的局部变量。python不会像函数调用那样引发错误,而是直接跳转到全局命名空间以执行查找(并忽略内置名称)。在所有其他情况下(非局部变量),正常的LEGB查找顺序适用


这确实有点违反直觉,我想说的是,如果不是完全违反了最不出人意料的规则,它也会起到推动作用。

答案在官方文件中给出了非常详细的内容。问题的关键是

…另一方面,对名称的实际搜索是在运行时动态完成的-但是,语言定义在“编译”时正朝着静态名称解析的方向发展,所以不要依赖于动态名称解析!(事实上,局部变量已经是静态确定的。)

当您在类定义中时(执行时是最内部的作用域),将应用动态名称解析。因此,您将看到
a
的全局值的打印输出

如果名称解析是静态的,如在函数定义中,则名称
a
即使在print语句中也会被识别为本地名称。这就是为什么在分配给函数之前不能在函数中打印
a

课程主体范围的规则在以下章节中有所提及:

类定义块和参数在名称解析上下文中是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则,但在全局命名空间中查找未绑定的局部变量除外

让我们仔细分析最后一句话,因为它完全涵盖了最后两个示例。首先,在这个上下文中,什么是未绑定的局部变量?类主体创建一个新的名称空间,就像输入一个函数一样。如果名称绑定在类主体的某个位置,它就是一个局部变量。这是静态确定的,如上所述。如果如果在首次绑定名称之前尝试引用该名称,则有一个未绑定的局部变量。python不会像函数调用那样引发错误,而是直接跳转到全局命名空间以执行查找(并忽略内置名称)。在所有其他情况下(非局部变量),正常的LEGB查找顺序适用


这确实有点违反直觉,我认为它会推动,如果不是完全违反了最小惊喜的规则。

这几乎就是它应该表现的样子。你读过哪些文档?这几乎就是它的表现