我希望能够创建具有从字典动态定义的实例属性的 python 对象。我知道至少有两种方法可以做到这一点,简单地调用setattr(self, k, v)
或调用setattr(FooClass, k, property(lambda x: v))
. 第一个选项工作得很好,但我不能让第二个做我想做的事。考虑以下代码
class A(object):
def __init__(self, d):
for k in d:
setattr(A, k, property(lambda x: ('Value(%s)' % d[k], 'Key(%s)' % k, d)))
>>> a = A({'foo':'1','bar':'2'})
>>> a.foo
('Value(2)', 'Key(bar)', {'bar': '2', 'foo': '1'})
为什么不a.foo
返回foo
字典中的值?property
对象及其吸气剂是不同的。
>>> A.foo, A.bar
<property at 0x1106c1d60>, <property at 0x1106c1d08>
>>> A.foo.__get__
<method-wrapper '__get__' of property object at 0x1106c1d60>
>>> A.bar.__get__
<method-wrapper '__get__' of property object at 0x1106c1d08>
关于 SO 有很多相关的问题,最好的一个是这个(如何动态地将属性添加到类?)和Eevee
答案。不过,这并不能完全解释这种行为。
这些处理类实例上的属性,而不是类Property
上的对象
更新
感谢 Martijn Pieters 的帮助(答案)。答案在于 lambda 函数的范围。
>>> A.bar.fget.__closure__[0], A.foo.fget.__closure__[0]
<cell at 0x1106c63d0: dict object at 0x7f90f699fe80>, <cell at 0x1106c63d0: dict object at 0x7f90f699fe80>
工作版本:
class A(object):
def __init__(self, d):
for k, v in d.items():
setattr(A, k, property(lambda x, v=v, k=k: ('Value(%s)' % v, 'Key(%s)' % k, d)))
尽管这实际上修改了类的每个实例以具有相同的属性值。
课:
小心
lambda
循环内的函数