目标是创建一个行为类似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__方法中提出了一个替代方案?
为了回答你的问题,你需要一个来自dict的只读属性作为不可变数据源:
目标是创建一个行为类似db结果集的模拟类。
例如,如果一个数据库查询返回一个dict表达式,
{'ab':100, 'cd':200},那么我将看到
> > > dummy.ab
One hundred.
我将演示如何使用collections模块中的namedtuple来实现这一点:
import collections
data = {'ab':100, 'cd':200}
def maketuple(d):
'''given a dict, return a namedtuple'''
Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
return Tup(**d)
dummy = maketuple(data)
dummy.ab
返回100
这里有一个解决方案:
允许将属性名指定为字符串,因此它们可以来自一些外部数据源,而不是全部列在程序中。
在定义类时添加属性,而不是每次创建对象时添加属性。
定义类之后,你只需动态地向它添加一个属性:
setattr(SomeClass, 'propertyName', property(getter, setter))
下面是一个完整的例子,用Python 3测试过:
#!/usr/bin/env python3
class Foo():
pass
def get_x(self):
return 3
def set_x(self, value):
print("set x on %s to %d" % (self, value))
setattr(Foo, 'x', property(get_x, set_x))
foo1 = Foo()
foo1.x = 12
print(foo1.x)
最好的方法是定义__slots__。这样你的实例就不能有新的属性。
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
__slots__ = []
def __init__(self, ks, vs): self.update(zip(ks, vs))
def __getattr__(self, key): return self[key]
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
打印12
c.ab = 33
'C'对象没有属性'ab'