【问题标题】:How to access 'global' variable from subpackage?如何从子包中访问“全局”变量?
【发布时间】:2026-01-31 05:30:01
【问题描述】:

我目前正在尝试发布一些更大的项目。这就是我决定使用子模块的原因。看一下项目结构:

/sandbox              
    __init.py__
    constants.py

    /sub1
        __init__.py
        foo.py

在我的constants.py 文件中,我声明了一个全局变量:

MYGLOBAL = 42

我想在foo.py 中使用它的值。该文件有 2 个测试函数:

def foofunc():
    return 'I am foo.'

def constfunc():
    return 'I am {MYGLOBAL}.'

另外,我把这段代码放到/sandbox/sub1/__init__.py

from .foo import *
from sandbox.constants import *

现在,当我使用我的解释器时,我会尝试像这样使用这两个函数:

>> import sandbox.sub1
>> sandbox.sub1.foofunc()
'I am foo.'
>> sandbox.sub1.MYGLOBAL
42
>> sandbox.sub1.constfunc()
NameError: name 'MYGLOBAL' is not defined

据我了解,全局变量与函数在同一个命名空间中,但不知何故函数看不到它。

如何访问它? Python 版本是 3.6。

谢谢!

【问题讨论】:

    标签: python python-3.x packaging


    【解决方案1】:

    “全局”变量在 Python 中并不是真正的全局变量,仅在定义它们的模块的命名空间中可用。

    您应该在foo.py 中导入constants,以便MYGLOBAL 可以作为constants 模块对象的属性使用:

    from sandbox import constants
    def constfunc():
        return f'I am {constants.MYGLOBAL}.'
    

    【讨论】:

    • 谢谢,所以我想我误解了__init__.py 文件的用途。我尝试使用它们类似于 cpp 中的头文件。有什么可以放什么不放的经验法则吗?
    【解决方案2】:

    这个非常好的问题的一个迟到的答案(赞成)。

    这里有一件事可以让您更好地了解正在发生的事情:

    >>> sandbox.sub1.constfunc.__module__
    'sandbox.sub1.foo'
    
    >>> sandbox.sub1.constfunc.__globals__['MYGLOBAL']
    KeyError: 'MYGLOBAL'
    
    >>> sandbox.sub1.foo.MYGLOBAL = 5
    
    >>> sandbox.sub1.constfunc.__globals__['MYGLOBAL']
    5
    
    >>> sandbox.sub1.constfunc()
    'I am 5'
    

    换句话说,

    “这个函数实际上属于哪个模块,因此会查看哪些模块全局变量可供它使用?”

    您错误地认为它是sandbox.sub1,但实际上它是sandbox.sub1.foo。原因是因为函数的“父模块”是随函数对象携带的,即使这个函数对象被复制到另一个模块的“工作区”。

    有了这种理解,您可以做几件事。一个是根据此处的另一个答案。另一种解决方案可能是在__init__.py 中执行此操作:

    from . import foo
    from .. import constants
    foo.MYGLOBAL = constants.MYGLOBAL
    

    等等

    注意事项:

    1. 需要注意的一点是from X import Y as Z 语法实际上等同于说Z = X.Y。这是一个重要见解的原因是因为它可以帮助您意识到 Z 本身并不是一个“参考”:如果您更改 X.Y,Z 将不会自动更新

      如果将 X 中的 Y 更改为另一个值,并且想要更新 Z,则需要使用“from X import Y as Z”语法重新导入它。

    2. 有趣的是,你不能简单地这样做:

      sandbox.sub1.constfunc.__module__ = sandbox.constants
      

      并且只是期望将函数的模块全局变量“替换”为sandbox.constants 的变量。原因似乎是 constfunc.__module__ 属性只是原始父模块的“副本”,更改它不会影响绑定到函数的实际父模块(即,它不会动态“重新绑定”函数到另一个模块;不过,由于这是一个内部变量,它可能会在未来的 python 版本中改变。谁知道。不要依赖它。)。

      另外,__module__ 属性不用于为函数创建constfunc.__globals__ 字典;这似乎总是直接反映“父模块”的全局字典。所以简单地替换 sandbox.sub1.constfunc.__module__ 变量不会自动替换 constfunc.__globals__ 字典,但更新 sandbox.sub1.foo 会。

    【讨论】: