我可以使用 __init__.py 来定义全局变量吗?

Posted

技术标签:

【中文标题】我可以使用 __init__.py 来定义全局变量吗?【英文标题】:Can I use __init__.py to define global variables? 【发布时间】:2010-11-25 21:02:07 【问题描述】:

我想定义一个常量,它应该在包的所有子模块中都可用。我认为最好的位置是在根包的__init__.py 文件中。但我不知道该怎么做。假设我有几个子包,每个子包都有几个模块。如何从这些模块中访问该变量?

当然,如果这是完全错误的,并且有更好的选择,我很想知道。

【问题讨论】:

【参考方案1】:

您应该可以将它们放入__init__.py。一直都是这样。

mypackage/__init__.py

MY_CONSTANT = 42

mypackage/mymodule.py

from mypackage import MY_CONSTANT
print "my constant is", MY_CONSTANT

然后,导入我的模块:

>>> from mypackage import mymodule
my constant is 42

不过,如果您确实有常量,则将它们放在单独的模块(constants.py、config.py、...)中是合理的(可能是最佳实践),然后如果您希望将它们放入包中命名空间,导入它们。

mypackage/__init__.py

from mypackage.constants import *

不过,这不会自动在包模块的命名空间中包含常量。包中的每个模块仍然必须从mypackagemypackage.constants 显式导入常量。

【讨论】:

这应该是公认的答案。如果您使用的是 Python 2.5 或更高版本,您还可以使用显式相对导入以及描述的 here:from . import MY_CONSTANT 第二种方式不是只适用于常量吗? from mypackage.constants import * 将在每个子模块中放置 MY_CONSTANT 的副本,而不是对同一变量的引用 @hardmooth:不完全是。这些值是通过引用复制的,因此如果您要在任何模块中对MY_CONSTANT 进行变异,它会在任何地方发生变异。如果您要在任何模块中重新分配MY_CONSTANT,它只会影响该模块。如果这是您的意图,则必须按属性引用,即mypackage.constants.MY_CONSTANT 该示例的一个问题是,如果您在MY_CONSTANT = 42 之前在__init__.py 中导入mymodule.py,则会失败,因为导入mymodule.pyMY_CONSTANT 尚未定义。所以需要将MY_CONSTANT = 42移到import mymodule上方 变量怎么样?它询问的是变量,而不是常量。 python如何处理包内的变量?如果包内的变量已更改怎么办?【参考方案2】:

你不能那样做。您必须明确地将常量导入每个单独模块的命名空间。实现这一点的最佳方法是在“配置”模块中定义常量并将其导入任何需要它的地方:

# mypackage/config.py
MY_CONST = 17

# mypackage/main.py
from mypackage.config import *

【讨论】:

是的,我想要一个配置文件。我只是认为 init.py 会是一个好地方。您的解决方案听起来像是标准做法。是吗? 好点。我没有意识到问题是将常量自动放置在所有包模块的命名空间中。 但是每次脚本导入config.py,里面的代码都会被执行。如果 config.py 中的代码只运行一次,你有什么建议?假设我正在读取 config.py 中的 settings.json 文件,我不想每次导入 config.py 时都打开()它。 @UGS 这不是 Python 的工作方式。每个模块只执行一次。第二次导入时,模块已经缓存在sys.modules中。 @FerdinandBeyer 哎呀!我忘了提到我是从几个脚本而不是同一个脚本中导入 config.py 的。假设 a.py 正在导入 config.py & b.py 而 b.py 正在导入 config.py。我想知道是否可以确保 config.py 中的代码只执行一次。【参考方案3】:

您可以在任何地方定义全局变量,但这是一个非常糟糕的主意。导入__builtin__ 模块并修改或添加该模块的属性,突然你有了新的内置常量或函数。事实上,当我的应用程序安装 gettext 时,我在所有模块中都获得了 _() 函数,而无需导入任何内容。所以这是可能的,但当然只适用于应用程序类型的项目,而不适用于可重用的包或模块。

而且我想没有人会推荐这种做法。命名空间有什么问题?所述应用程序具有版本模块,因此我可以使用“全局”变量,例如 version.VERSIONversion.PACKAGE_NAME 等。

【讨论】:

【参考方案4】:

只是想补充一点,可以使用 config.ini 文件使用常量,并使用 configparser 库在脚本中进行解析。这样,您可以在多种情况下使用常量。例如,如果您有两个单独的 url 请求的参数常量,只需像这样标记它们:

mymodule/config.ini
[request0]
conn = 'admin@localhost'
pass = 'admin'
...

[request1]
conn = 'barney@localhost'
pass = 'dinosaur'
...

我发现 Python 网站上的文档非常有用。我不确定 Python 2 和 3 之间是否有任何区别,所以这里是两者的链接:

对于 Python 3:https://docs.python.org/3/library/configparser.html#module-configparser

对于 Python 2:https://docs.python.org/2/library/configparser.html#module-configparser

【讨论】:

以上是关于我可以使用 __init__.py 来定义全局变量吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在(子)模块中使用 __init__.py 来定义命名空间?

(Python第八天)模块

Python module中的全局变量

Python __all__变量用法

从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?

__init__.py的用法