我正在模仿ConfigParser
模块的行为来编写一个高度专业化的解析器,该解析器利用我使用的特定应用程序的配置文件中的一些明确定义的结构。配置文件的几个部分包含数百个以Variable_
或为前缀的变量和例程映射Routine_
,如下所示:
[Map.PRD]
Variable_FOO=LOC1
Variable_BAR=LOC2
Routine_FOO=LOC3
Routine_BAR=LOC4
...
[Map.SHD]
Variable_FOO=LOC1
Variable_BAR=LOC2
Routine_FOO=LOC3
Routine_BAR=LOC4
...
我想保持ConfigParser
每个部分存储为单个字典的基本结构,因此用户仍然可以访问经典语法:
config.content['Mappings']['Variable_FOO'] = 'LOC1'
但也可以使用深入到本节的简化 API:
config.vmapping('PRD')['FOO'] = 'LOC1'
config.vmapping('PRD')['BAR'] = 'LOC2'
config.rmapping('PRD')['FOO'] = 'LOC3'
config.rmapping('PRD')['BAR'] = 'LOC4'
目前,我正在通过将部分存储在一个特殊的子类中dict
来实现这一点,我已经在该子类中添加了一个prefix
属性。解析器的variable
and属性将-like 对象的属性设置为or然后修改句柄的属性,将前缀与键粘合在一起以访问适当的项目。它正在工作,但涉及大量样板来实现所有相关的细节,如支持迭代。routine
prefix
dict
'Variable_'
'Routine_'
__getitem__
__setitem__
dict
我想我的理想解决方案是放弃子类dict
,并让variable
androutine
属性以某种方式呈现dict
下面没有前缀的普通对象的“视图”。
更新
这是我实施的解决方案,主要基于@abarnet 的回答:
class MappingDict(object):
def __init__(self, prefix, d):
self.prefix, self.d = prefix, d
def prefixify(self, name):
return '{}_{}'.format(self.prefix, name)
def __getitem__(self, name):
name = self.prefixify(name)
return self.d.__getitem__(name)
def __setitem__(self, name, value):
name = self.prefixify(name)
return self.d.__setitem__(name, value)
def __delitem__(self, name):
name = self.prefixify(name)
return self.d.__delitem__(name)
def __iter__(self):
return (key.partition('_')[-1] for key in self.d
if key.startswith(self.prefix))
def __repr__(self):
return 'MappingDict({})'.format(dict.__repr__(self))
class MyParser(object):
SECTCRE = re.compile(r'\[(?P<header>[^]]+)\]')
def __init__(self, filename):
self.filename = filename
self.content = {}
lines = [x.strip() for x in open(filename).read().splitlines()
if x.strip()]
for line in lines:
match = re.match(self.SECTCRE, line)
if match:
section = match.group('header')
self.content[section] = {}
else:
key, sep, value = line.partition('=')
self.content[section][key] = value
def write(self, filename):
fp = open(filename, 'w')
for section in sorted(self.content, key=sectionsort):
fp.write("[%s]\n" % section)
for key in sorted(self.content[section], key=cpfsort):
value = str(self.content[section][key])
fp.write("%s\n" % '='.join([key,value]))
fp.write("\n")
fp.close()
def vmapping(self, nsp):
section = 'Map.{}'.format(nsp)
return MappingDict('Variable', self.content[section])
def rmapping(self, nsp):
section = 'Map.{}'.format(nsp)
return MappingDict('Routine', self.content[section])
它是这样使用的:
config = MyParser('myfile.cfg')
vmap = config.vmapping('PRD')
vmap['FOO'] = 'LOC5'
vmap['BAR'] = 'LOC6'
config.write('newfile.cfg')
由此产生newfile.cfg
的反映LOC5
和LOC6
变化。