Python 如何实现复合配置变量,或;模块级属性?“;
如果我有一个配置文件,Python 如何实现复合配置变量,或;模块级属性?“;,python,python-2.7,Python,Python 2.7,如果我有一个配置文件,default.py: HOST = 'localhost' PORT = 8080 PROTOCOL = 'http' if PORT != 443 else 'https' ROOT_STR = '%s://%s:%s' % (PROTOCOL, HOST, PORT) 然后我有一个local.py,它覆盖默认变量以适应本地环境: PORT = 443 我如何动态地重新生成协议和根目录,就好像它们是类的属性一样,而是在模块的根目录级别 通过更改一个变量得到的配置将为
default.py
:
HOST = 'localhost'
PORT = 8080
PROTOCOL = 'http' if PORT != 443 else 'https'
ROOT_STR = '%s://%s:%s' % (PROTOCOL, HOST, PORT)
然后我有一个local.py
,它覆盖默认变量以适应本地环境:
PORT = 443
我如何动态地重新生成协议
和根目录
,就好像它们是类的属性一样,而是在模块的根目录级别
通过更改一个变量得到的配置将为:
HOST = 'localhost'
PORT = 443
PROTOCOL = 'https'
ROOT_STR = 'https://localhost:443'
预期行为应为类属性建模:
class Configuration(object):
def __init__(self):
self.host = 'localhost'
self.port = 8080
@property
def protocol(self):
return 'https' if self.port == 443 else 'http'
@property
def root_str(self):
return '%s://%s:%s' % (self.protocol, self.host, self.port)
...
...
>>> c = Configuration()
>>> c.port = 443
>>> c.root_str
'https://localhost:443'
有没有比修改AST更干净的方法
想法?您可以将默认导入中的
添加到local.py
文件的顶部。这将使default.py
中的所有变量和函数都可用,就像它们在local.py
中一样。我使用了一个自定义配置类和元类,大致实现如下
import re
import types
CONST_VARS = re.compile(r'[A-Z]+[_A-Z]*')
# Configuration primitives that allow for dynamic
# variable compilation for default/local/dev/etc settings files.
class ConfigType(type):
def __init__(cls, name, bases, odict):
if not hasattr(cls, 'vars'):
cls.vars = {}
reg_vars = filter(lambda x: CONST_VARS.match(x[0]), odict.items())
for k, v in reg_vars:
cls.vars[k] = v
delattr(cls, k)
super(ConfigType, cls).__init__(name, bases, cls.vars)
class Configuration(object):
__metaclass__ = ConfigType
def __init__(self):
pass
def __setattr__(self, key, value):
self.vars[key] = value
def __getattr__(self, item):
ret = self.vars.get(item)
if isinstance(ret, types.FunctionType):
return ret(self)
return ret
def as_module(self):
mod_vars = {}
for k, v in self.vars.items():
if isinstance(v, types.FunctionType) and callable(v):
v = v(self)
mod_vars[k] = v
return type('Configuration', (object,), mod_vars)
然后,如果我创建两个Configuration
对象:
设置/default.py
class DatabaseConfig(Configuration):
MYSQL_DB = 'DEFAULT'
MYSQL_USER = os.environ.get('MYSQL_USER')
MYSQL_PASS = os.environ.get('MYSQL_PASS')
MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost')
MYSQL_PORT = os.environ.get('MYSQL_PORT', 3306)
def MYSQL_URI(self):
return 'mysql://{user}:{password}@{server}:{port}'.format(
user=self.MYSQL_USER,
password=self.MYSQL_PASS,
server=self.MYSQL_HOST,
port=self.MYSQL_PORT )
def SQLALCHEMY_DATABASE_URI(self):
return str(self.MYSQL_URI) + '/' + self.MYSQL_DB
DatabaseConfig()
class LocalConfig(Configuration):
MYSQL_DB = 'mycoolstuff'
MYSQL_USER = 'root'
MYSQL_PASS = 'rootpass'
MYSQL_HOST = '172.17.42.1' # Docker
MYSQL_PORT = 3306
Config = LocalConfig()
设置/local.py
class DatabaseConfig(Configuration):
MYSQL_DB = 'DEFAULT'
MYSQL_USER = os.environ.get('MYSQL_USER')
MYSQL_PASS = os.environ.get('MYSQL_PASS')
MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost')
MYSQL_PORT = os.environ.get('MYSQL_PORT', 3306)
def MYSQL_URI(self):
return 'mysql://{user}:{password}@{server}:{port}'.format(
user=self.MYSQL_USER,
password=self.MYSQL_PASS,
server=self.MYSQL_HOST,
port=self.MYSQL_PORT )
def SQLALCHEMY_DATABASE_URI(self):
return str(self.MYSQL_URI) + '/' + self.MYSQL_DB
DatabaseConfig()
class LocalConfig(Configuration):
MYSQL_DB = 'mycoolstuff'
MYSQL_USER = 'root'
MYSQL_PASS = 'rootpass'
MYSQL_HOST = '172.17.42.1' # Docker
MYSQL_PORT = 3306
Config = LocalConfig()
当我导入LocalConfig
时,我的变量将自动计算,所有函数作为属性,所有类属性作为变量
>>> import settings.default
>>> from settings.local import Config
>>> Config.MYSQL_PORT
3306
>>> Config.MYSQL_URI
mysql://root:rootpass@172.17.42.1:3306
@RNar可能重复不幸的是,它们不同-问题不是访问变量,而是动态地重新生成后续变量,这些变量在赋值中使用了更改的变量。我不太明白您想尝试什么,但是让local.py
只在本地存在是否更有意义,然后使用一个try…除了importorror
之外,如果存在local.py
值,则导入该值,否则保留全局默认值?如果范围和限制得当,甚至可能是导入*
的罕见好用例之一。@Two Bitalchest我用一个类级属性更新了这个问题,以演示预期的行为。您提供的类似单例的类是否适合您的要求?这种“类型”有效,但是所有计算的变量都不会根据local.py中的更改重新计算。例如:如果C=A+B
在default
中,其中A
和B
也被定义,如果我在local
中更改A
,它还应该更新C
的值,这是非常正确的。一种方法是使用一个可以扩展的类(class BaseSettings(object):…
在默认情况下,然后是底部的SETTINGS=BaseSettings()
,然后是class LocalSettings(BaseSettings):…
在本地文件中导入后,然后是SETTINGS=LocalSettings()
)或者将动态逻辑放入一个管理器中,通过它您可以使用这些设置。可能还有更多的方法。