如何使用 __setattr__ 和 __getattr__ 映射 INI 值?
我想把一个 INI 文件映射成一个 Python 对象。如果这个文件里有:
[UserOptions]
SampleFile = sample.txt
SamplePort = 80
SampleInt = 1
Sample = Aja
SampleDate = 10/02/2008
那么我想要得到:
c = Configuration('sample.ini')
c.UserOptions.SamplePort = 90
我在尝试使用 setattr,但是遇到了递归错误。
这是我现在的代码:
class Configuration:
def __init__ (self, fileName):
cp = SafeConfigParser()
cp.read(fileName)
self.__parser = cp
self.fileName = fileName
def __getattr__ (self, name):
if name in self.__parser.sections():
return Section(name, self.__parser)
else:
return None
def __str__ (self):
p = self.__parser
result = []
result.append('<Configuration from %s>' % self.fileName)
for s in p.sections():
result.append('[%s]' % s)
for o in p.options(s):
result.append('%s=%s' % (o, p.get(s, o)))
return '\n'.join(result)
class Section:
def __init__ (self, name, parser):
self.__name = name
self.__parser = parser
def __getattr__ (self, name):
if self.__dict__.has_key(name): # any normal attributes are handled normally
return __getattr__(self, item)
else:
return self.__parser.get(self.name, name)
def __setattr__(self, item, value):
"""Maps attributes to values.
Only if we are initialised
"""
if self.__dict__.has_key(item): # any normal attributes are handled normally
dict.__setattr__(self, item, value)
else:
self.__parser.set('UserOptions',item, value)
现在我想知道为什么在 self.__parser.set('UserOptions', item, value)
这行代码里会出现错误。我查阅了 Python 的文档,但还是不太明白该怎么做。我怀疑我需要存储一个字典,里面包含字段名称,然后先去查这个字典,但我该怎么做呢?
2 个回答
4
你的问题出在 Section.__init__
这个地方。当你设置 self.__name = name
时,它会调用你的 __setattr__
方法,但在 __dict__
中找不到这个键,所以它就去找其他地方。
self.__parser.set('UserOptions',item, value)
现在它需要 self.__parser
。
但是这个还没有被设置好。所以它尝试用 __getattr__
来获取这个值。结果又去找 self.__parser
,但这个还是没有设置好。所以又用 __getattr__
来获取。你明白我的意思了吧 :-)
避免这种情况的一种方法是在 Section.__setattr__
中加一个条件,像这样:
if item.startswith('_') or self.__dict__.has_key(item):
^^^^^^^^^^^^^^^^^^^^^^^
...
这样可以确保在初始化时 __name
和 __parser
都被正确设置。
4
你想要获取请求中的各个部分。但其实更简单的方法是遍历这些部分和选项,然后在 __init__
中把它们作为属性添加进去。我修改了我的例子,以支持 setattr。你的问题在 这里 有解释,你在 __setattr__
中给属性赋值,而应该使用 __dict__
。
from ConfigParser import SafeConfigParser
class Section:
def __init__(self, name, parser):
self.__dict__['name'] = name
self.__dict__['parser'] = parser
def __setattr__(self, attr, value):
self.__dict__[attr] = str(value)
self.parser.set(self.name, attr, str(value))
class Configuration(object):
def __init__(self, fileName):
self.__parser = SafeConfigParser()
self.__parser.read(fileName)
self.fileName = fileName
for section in self.__parser.sections():
setattr(self, section, Section(section, self.__parser))
for option in self.__parser.options(section):
setattr(getattr(self, section), option,
self.__parser.get(section, option))
def __getattr__(self, attr):
self.__parser.add_section(attr)
setattr(self, attr, Section(attr, self.__parser))
return getattr(self, attr)
def save(self):
f = open(self.fileName, 'w')
self.__parser.write(f)
f.close()
c = Configuration('config.ini')
print dir(c) -> will print all sections
print dir(c.UserOptions) -> will print all user options
print c.UserOptions.sampledate
c.new.value = 10
c.save()