在 C++ 下,有一个众所周知的问题,叫做全局/静态变量初始化顺序惨败,因为 C++ 无法决定哪个全局/静态变量将在编译单元中首先初始化,
我认为该声明突出了 Python 和 C++ 之间的一个关键区别:在 Python 中,没有不同的编译单元之类的东西。我的意思是,在 C++ 中(如您所知),两个不同的源文件可能完全相互独立地编译,因此如果您比较文件 A 中的一行和文件 B 中的一行,没有什么可说的您将首先进入程序。这有点像多线程的情况:你不能说线程 1 中的特定语句是在线程 2 中的特定语句之前还是之后执行。你可以说 C++ 程序是并行编译的。
相比之下,在 Python 中,执行从一个文件的顶部开始,并以明确定义的顺序执行文件中的每个语句,并在导入文件的位置分支到其他文件。实际上,您几乎可以将import 指令视为#include,这样您就可以识别程序中所有源文件中所有代码行的执行顺序。 (嗯,比这复杂一点,因为模块只有在第一次导入时才会真正执行,还有其他原因。)如果 C++ 程序是并行编译的,Python 程序是串行解释的。
您的问题还涉及 Python 中模块的更深层含义。 Python 模块(即单个.py 文件中的所有内容)是一个实际对象。在单个源文件中在“全局”范围内声明的所有内容实际上都是该模块对象的属性。 Python 中没有真正的全局作用域。 (Python 程序员经常说“全局”,实际上语言中有一个 global 关键字,但它总是指当前模块的顶层。)我可以看到这是一个有点奇怪的概念习惯于来自 C++ 背景。对我来说,我需要一些时间来适应,来自 Java,在这方面,Java 比 C++ 更类似于 Python。 (Java 中也没有全局作用域)
我会提到,在 Python 中,使用变量而不知道它是否已被初始化/定义是完全正常的。好吧,也许不正常,但至少在适当的情况下可以接受。在 Python 中,尝试使用未定义的变量会引发 NameError;您不会像在 C 或 C++ 中那样获得任意行为,因此您可以轻松处理这种情况。您可能会看到这种模式:
try:
duck.quack()
except NameError:
pass
如果duck 不存在,它什么也不做。实际上,你会更常见的是
try:
duck.quack()
except AttributeError:
pass
如果duck 没有名为quack 的方法,它什么也不做。 (AttributeError 是您尝试访问对象的属性时遇到的那种错误,但该对象没有该名称的任何属性。)这就是 Python 中通过类型检查的原因:我们认为如果我们需要鸭子做的就是嘎嘎,我们可以让它嘎嘎叫,如果它叫,我们不在乎它是否真的是一只鸭子。 (这叫做鸭子打字 ;-)