从配置文件中正确获取时间差值的方法
我有一个配置文件,里面存储了一些统计数据:迭代次数和时间差对象。我想找到一种既符合Python风格又安全的方法来评估这些值。
这个配置文件的样子是这样的(test_config.cfg):
[Section]
option1 = (6, datetime.timedelta(0, 9, 520000))
option2 = (4, datetime.timedelta(0, 8, 510000))
当我使用 RawConfigParser.set('Section', 'option', (i, t_delta))
时,time.timedelta()
这个条目就会出现,其中 t_delta
只是两个操作之间的时间间隔,而 i
是迭代的次数。当我从配置中读取这些值时,它们会以字符串的形式返回。我想把它们评估回它们最初的样子。我尝试过的方法可以工作,但我觉得应该有一种更安全、更符合Python风格的方法:
import ConfigParser
import datetime
config = ConfigParser.RawConfigParser()
config.read('test_config.cfg')
stats = config.get('Section', 'option1')
# The obvious way is with eval() but makes me very uncomfortable using it
iterations = eval(stats)[0] # 6
duration = eval(stats)[1] # 0:00:09.520000
# The ugly way is with stripping and splitting
duration_tuple = tuple(int(i) for i in stats.split('(')[-1].strip(')').split(','))
duration = datetime.timedelta(*duration_tuple)
iterations = int(stats.split(',')[0].lstrip('('))
print iterations # 6
print duration # 0:00:09.520000
那么有没有更好的方法呢?最终有没有办法在设置配置时只使用时间差对象的“元组”?比如 (0, 9, 520000)
而不是 datetime.timedelta(0, 9, 520000)
。这样我就可以轻松使用 ast.literal_eval()
了。谢谢!
2 个回答
使用
RawConfigParser.set('Section', 'option', (i, [t_delta.days, t_delta.seconds, t_delta.microseconds]))
你可以把你的配置文件整理成这样:
[Section]
option1 = [6, [0, 9, 520000]]
option2 = [4, [0, 8, 510000]]
然后你可以使用 json 来解析 config.get
返回的字符串:
import ConfigParser
import datetime
import json
config = ConfigParser.RawConfigParser()
config.read('test_config.cfg')
stats = config.get('Section', 'option1')
stats = json.loads(stats)
iterations = stats[0] # 6
duration = datetime.timedelta(*stats[1]) # 0:00:09.520000
print iterations # 6
print duration # 0:00:09.520000
这其实和使用 ast.literal_eval
的想法是一样的。
要解析一个 JSON 格式的配置文件:
不过,如果可以选择的话,我觉得直接放弃 ConfigParser
,只用 JSON
会更简单。例如,如果配置文件看起来是这样的:
{"option2": [4, [0, 8, 510000]], "option1": [6, [0, 9, 520000]]}
那么要把它读回 Python,你可以使用:
import datetime
import json
with open('test_config.cfg', 'r') as f:
config = json.load(f)
stats = config['option1']
iterations = stats[0] # 6
duration = datetime.timedelta(*stats[1]) # 0:00:09.520000
print iterations # 6
print duration # 0:00:09.520000
要把配置保存到一个 JSON 格式的文件:
config = {'option': [i, [t_delta.days, t_delta.seconds, t_delta.microseconds]}
with open('test_config.json', 'w') as f:
json.dump(config, f)
你可以把时间差(timedelta)转换成秒数(用浮点数表示,这样可以保留小数部分)。
RawConfigParser.set('Section', 'option1', '%s,%s' % (i, t_delta.total_seconds()))
现在你的代码部分看起来是这样的:
[Section]
option1 = (6, 9.520000)
option2 = (4, 8.510000)
而且你可以不使用eval这个函数:
stats = RawConfigParser.get('Section', 'option1').split(',')
iterations = int(stats[0]) # 6
duration = datetime.timedelta(seconds=float(stats[1])) # 0:00:09.520000
(编辑)
这里有一个示例,展示了如何用ast.literal_eval这个方法和split/cast这两种方式来处理。使用literal_eval有点风险,因为不怀好意的人可能会执行你不想让他们执行的代码,但它仍然是一个合理的选择。而split/cast方法对输入的要求更严格,只接受特定格式的数据:
>>> from ConfigParser import RawConfigParser
>>> import datetime
>>> import ast
>>>
>>> i = 6
>>> t_delta = datetime.timedelta(0, 9, 520000)
>>>
>>> config = RawConfigParser()
>>> config.add_section('Section')
>>> config.set('Section', 'option_for_eval', (i, t_delta.total_seconds()))
>>> config.set('Section', 'option_for_cast', '%s,%f' % (i, t_delta.total_seconds()))
>>> config.write(open('/tmp/config.ini', 'w'))
>>>
>>> config = RawConfigParser()
>>> config.read('/tmp/config.ini')
['/tmp/config.ini']
>>> option_for_eval = config.get('Section', 'option_for_eval')
>>> option_for_eval
'(6, 9.52)'
>>> i, t_delta = ast.literal_eval(option_for_eval)
>>> i, t_delta
(6, 9.52)
>>>
>>> option_for_cast = config.get('Section', 'option_for_cast')
>>> stat = option_for_cast.split(',')
>>> i = int(stat[0])
>>> t_delta = datetime.timedelta(seconds=float(stat[1]))
>>> i, t_delta
(6, datetime.timedelta(0, 9, 520000))
>>>