组织用于从配置文件中使用常量进行测试的代码

时间:2013-06-19 09:55:21

标签: python

我的应用程序从配置文件中读取许多常量。然后在整个程序的各个地方使用这些常量。这是一个例子:

import ConfigParser

config = ConfigParser.SafeConfigParser()
config.read('my.conf')
DB_HOST = config.get('DatabaseInfo', 'address')
DB_PORT_NUMBER = config.getint('DatabaseInfo', 'port_number')
DB_NUMBER = config.getint('DatabaseInfo', 'db_number')
IN_SERVICE = config.get('ServerInfo', 'in_service')
IN_DATA = config.get('ServerInfo', 'in_data')

等...

然后我在程序中定义了使用这些常量的函数。这是一个例子:

def connect_to_db():
    return get_connection(DB_HOST, DB_PORT_NUMBER, DB_NUMBER)

有时,当我测试或使用REPL时,我不想使用配置文件中定义的值。

所以我改为将常量定义为参数:

def connect_to_db(db_host, db_port_number, db_number):
    return get_connection(db_host, db_port_number, db_number)

然后当我的程序运行时,常量全部传入我的main,需要调用其他函数然后调用函数并创建需要常量的类(所有可能在不同的模块中) :

def main(db_host, db_port_number, db_number, in_service, in_data):
    intermediate_function(
        db_host, db_port_number, db_number, other, parameters, here
    )
    other_intermediate_function(in_service, in_data, more, params, here)
    # etc...

def intermediate_function(db_host, db_port_number, db_number):
    # Other processing...
    c = connect_to_db(db_host, db_port_number, db_number)
    # Continued...

if __name__ == '__main__':
    main(DB_HOST, DB_PORT_NUMBER, DB_NUMBER, IN_SERVICE, IN_DATA)

问题是,由于常量太多,这很快变得难以处理。如果我需要添加另一个常量,我有几个地方需要修改以确保我的代码不会中断。这是一场维护噩梦。

处理许多不同配置常量的Pythonic方法是什么,这样代码仍然易于修改并且易于测试?

1 个答案:

答案 0 :(得分:2)

我的想法非常简单,使用可选的关键字参数。 这是一个小例子,我在说什么:

# collect, the constants to a dictionary,
# with the proper key names
d = dict(a=1, b=2, c=3)

# create functions, where you want to use the constants
# and use the same key-names as in dictionary, and also
# add '**kwargs' at the end of the attribute list
def func1(a, b, d=4, **kwargs):
    print a, b, d

def func2(c, f=5, **kwargs):
    print c, f

# now any time, you use the original dictionary
# as an argument for one of these functions, this
# will let the function select only those keywords
# that are used later
func1(**d)
# 1 2 4
func2(**d)
# 3 5

这个想法允许您在原始字典中仅在一个地方修改常量列表。 所以这回到了你的想法:

这是您的configuration.py

# Your parsing, reading and storing functions goes here..

# Now create your dictionary
constants = dict(
    host  = DB_HOST,
    pnum  = DB_PORT_NUMBER,
    num   = DB_NUMBER,
    serv  = IN_SERVICE,
    data  = IN_DATA
)

这是您的other_file.py

import configuration as cf

def main(host, pnum, num, serv, data, **kwargs):
    intermediate_function(
        host, pnum, num, 'other', 'params', 'here'
    )

def intermediate_function(host, pnum, num, *args):
    pass

# Now, you can call this function, with your
# dictionary as keyword arguments
if __name__ == '__main__':
    main(**cf.constants)

虽然这有效,但我不建议使用此解决方案! 你的代码将难以维护,因为每次调用其中一个函数时,你传递常量字典,你只会看到“一个”参数:字典本身,它不是那么冗长。所以我相信,你应该考虑一个更好的代码架构,你使用更多确定性函数(返回“真实”值),并使用它们相互链接,所以你没有一直传递所有这些常量。但这是我的意见:))

修改

如果我上面提到的解决方案适合您,我也可以更好地了解如何存储和解析配置文件,并将其自动转换为字典。使用JSON而不是简单的.cfg文件:

这将是您的conf.json

{
    "host"  : "DB_HOST",
    "pnum"  : "DB_PORT_NUMBER",
    "num"   : "DB_NUMBER",
    "serv"  : "IN_SERVICE",
    "data"  : "IN_DATA"
}

您的configuration.py将如下所示:

import json

with open('conf.json') as conf:
    # JSON parser will convert it to dictionary
    constants = json.load(conf)