目标是创建一个行为类似db结果集的模拟类。
例如,如果一个数据库查询返回,使用dict表达式,{'ab':100, 'cd':200},那么我想看到:
>>> dummy.ab
100
一开始我想也许我可以这样做:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
但是cab返回一个属性对象。
用k = property(lambda x: vs[i])替换setattr行根本没有用。
那么,在运行时创建实例属性的正确方法是什么呢?
附注:我知道在如何使用__getattribute__方法中提出了一个替代方案?
对于那些来自搜索引擎的人,这里是我在谈论动态属性时寻找的两件事:
class Foo:
def __init__(self):
# we can dynamically have access to the properties dict using __dict__
self.__dict__['foo'] = 'bar'
assert Foo().foo == 'bar'
# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
def __init__(self):
self._data = {}
def __getattr__(self, key):
return self._data[key]
def __setattr__(self, key, value):
self._data[key] = value
bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'
如果你想放置动态创建的属性,__dict__是很好的。__getattr__只适合在需要该值时执行某些操作,例如查询数据库。set/get组合有助于简化对存储在类中的数据的访问(如上面的示例)。
如果您只想要一个动态属性,请查看property()内置函数。
我最近遇到了一个类似的问题,我提出的解决方案使用__getattr__和__setattr__为我想要它处理的属性,其他一切都传递给原始。
class C(object):
def __init__(self, properties):
self.existing = "Still Here"
self.properties = properties
def __getattr__(self, name):
if "properties" in self.__dict__ and name in self.properties:
return self.properties[name] # Or call a function, etc
return self.__dict__[name]
def __setattr__(self, name, value):
if "properties" in self.__dict__ and name in self.properties:
self.properties[name] = value
else:
self.__dict__[name] = value
if __name__ == "__main__":
my_properties = {'a':1, 'b':2, 'c':3}
c = C(my_properties)
assert c.a == 1
assert c.existing == "Still Here"
c.b = 10
assert c.properties['b'] == 10
这似乎是可行的(但见下文):
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
self.__dict__.update(self)
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
如果您需要更复杂的行为,请随意编辑您的答案。
edit
对于大型数据集,以下方法可能更节省内存:
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
def __getattr__(self,name):
return self[name]
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)