为了最小化整个程序中的硬编码值,我定义了一组常量,如下面的Constants.py
:
FOO = 42
HOSTNAME=socket.gethostname()
BAR = 777
所有想要使用这些常量的模块,只需执行import Constants
并立即使用Constants.FOO
。
现在其中一些常量可能取决于程序运行的实际主机。
因此,我想根据应用程序运行的实际环境有选择地覆盖其中一些。
第一次原油尝试看起来像以下Constants.py
:
FOO = 42
HOSTNAME=socket.gethostname()
if HOSTNAME == 'pudel':
BAR = 999
elif HOSTNAME == 'knork'
BAR = 888
else:
BAR = 777
虽然这样可行但是它会使特殊情况混乱,我想避免这种情况。
如果我在进行shell脚本编写,我会使用类似Constants.sh
的内容:
FOO = 42
HOSTNAME=$(hostname)
BAR = 777
# load host-specific constants
if [ -e Constants_${HOSTNAME}.sh ]; then
. Constants_${HOSTNAME}.sh
fi
和可选的Constants_pudel.sh
,如下所示:
BAR = 999
将公共常量保持在一起,并允许在单独的文件中轻松覆盖它们。
因为我不是在编写shell脚本而是编写python程序,所以我想知道如何获得相同的结果。
无济于事我试过像:
FOO = 42
HOSTNAME=socket.gethostname()
BAR = 777
try:
__import__('Constants_'+HOSTNAME)
except ImportError:
pass
和Constants_poodle.py
看起来像:
import Constants
Constants.BAR = 999
这可以在Constants_poodle
内正常工作,但是当我在另一个python文件中尝试import Constants
时,我会得到原始的Constants.BAR
。
除了根本不工作之外,使用__import__()
似乎非常难看,所以我想有一种正确的方法来覆盖特定设置的导出常量?
答案 0 :(得分:1)
您的解决方案有几个问题。首先,导入不会将导入的模块添加到当前名称空间。不确定python 3.x,但在python 2.x上你可以做类似的事情:
FOO = 42
BAR = 777
HOSTNAME=socket.gethostname()
try:
_imp = __import__('Constants_'+HOSTNAME)
_locals = locals()
for _name in dir(_imp):
if not _name.startswith('_'):
_locals[_name] = getattr(_imp, _name)
del _imp, _locals, _name
except ImportError:
pass
但是下一个问题是所有的constants_xxx.py文件都必须在python路径中。
我认为更适合您的替代解决方案是将配置放在用户目录中的.ini文件中,并使用ConfigParser(或yaml或xml,具体取决于您的口味)。
答案 1 :(得分:1)
您可以使用以下内容执行类似操作,该操作源自Activestate Constants in Python配方:
import os as _os
import socket as _socket
class _constants(object):
def __init__(self):
self.FOO = 42
self.HOSTNAME = _socket.gethostname()
self.BAR = 777
# load host-specific constants
hostconst = 'Constants_{}'.format(self.HOSTNAME)
if _os.path.exists(hostconst):
localdict = {}
execdict = self.__dict__.copy()
execdict['__builtins__'] = None
execfile(hostconst, execdict, localdict)
self.__dict__.update(localdict) # add/override attributes defined
def __setattr__(self, name, value):
self.__dict__[name] = value
# replace module entry in sys.modules[__name__] with instance of _constants
# (and create additional reference to module so it's not deleted --
# see Stack Overflow question: http://bit.ly/ff94g6)
import sys
_ref, sys.modules[__name__] = sys.modules[__name__], _constants()
if __name__ == '__main__':
import constants
print constants.FOO
print constants.HOSTNAME
print constants.BAR
因此,例如,如果_socket.gethostname()
返回'pudel',并且存在包含这些行的Constants_pudel
文件:
BAR = 999
FOO += 1
然后print
语句的输出将是:
43
pudel
999