【问题标题】:Python Module Initialization Order?Python模块初始化顺序?
【发布时间】:2011-03-06 03:38:04
【问题描述】:

我是来自 C++ 背景的 Python 新手。虽然我知道尝试使用我的旧 C++ 知识找到匹配的概念不是 Pythonic,但我认为这个问题仍然是一个普遍的问题:

在 C++ 下,有一个众所周知的问题,称为全局/静态变量初始化顺序失败,因为 C++ 无法决定哪个全局/静态变量将在编译单元中首先初始化,因此全局/静态变量取决于另一个在不同的编译单元中可能比其依赖对应物更早地初始化,并且当依赖开始使用依赖对象提供的服务时,我们将有未定义的行为。在这里我不想深入探讨 C++ 是如何解决这个问题的。 :)

在 Python 世界中,我确实看到了全局变量的使用,甚至在不同的 .py 文件中,我看到的一个典型用例是:在一个 .py 文件中初始化一个全局对象,在其他 .py 文件中,代码只是无所畏惧地开始使用全局对象,假设它必须已经在其他地方初始化,由于我上面指定的问题,在 C++ 下我绝对不能接受。

我不确定上述用例是否是Python中的常见做法(Pythonic),Python一般如何解决这种全局变量初始化顺序问题?

【问题讨论】:

    标签: python initialization


    【解决方案1】:

    Python 导入从头到尾执行新的 Python 模块。后续导入只会导致 sys.modules 中现有引用的副本,即使由于循环导入而仍在导入模块的中间。在循环导入存在之前已经初始化的模块属性(“全局变量”实际上是在模块范围内)。

    main.py:

    import a
    

    a.py:

    var1 = 'foo'
    import b
    var2 = 'bar'
    

    b.py:

    import a
    print a.var1 # works
    print a.var2 # fails
    

    【讨论】:

    • 所以Python全局变量初始化顺序是通过“import”语句显式执行和强制执行的,这是有道理的,因为C++没有办法显式指定编译单元依赖关系。感谢您澄清它!
    【解决方案2】:

    在 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 中通过类型检查的原因:我们认为如果我们需要鸭子做的就是嘎嘎,我们可以让它嘎嘎叫,如果它叫,我们不在乎它是否真的是一只鸭子。 (这叫做鸭子打字 ;-)

    【讨论】:

    • 这绝对是对我发布的问题的彻底解释,切中要害!谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-09
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多