我可以使用 __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 *
不过,这不会自动在包模块的命名空间中包含常量。包中的每个模块仍然必须从mypackage
或mypackage.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.py
时MY_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.VERSION
、version.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 来定义命名空间?