如何重写字典类对象的属性设置?
我正在写一个C语言的绑定,想要把一个C结构体中的一些用字符串索引的属性值封装起来。
我想把这些属性在Python中以字典的形式展示。
到目前为止,我已经有了一个叫做 get_properties
的函数,它可以把对象的所有属性作为一个字典返回。我在类定义中用 property
函数把它封装起来,这样我就可以像访问类属性一样来访问它:
(在类定义中)
class MyClass:
def get_properties(self):
...
properties = property(get_properties)
(示例)
>>> print myobj.properties
{'test': 5, 'test2': 'string'}
现在,我想以字典的方式来处理设置这些属性。我有一个叫 set_property
的包装函数,它接受一个字符串键和几种类型的值。
我尝试使用类 property
中的 set_properties
:
class MyClass:
def get_properties(self):
...
def set_property(self, key, value):
...
def set_properties(self, props):
[self.set_property(k, props[k]) for k in props]
properties = property(get_properties, set_properties)
它的工作方式如下:
>>> myobj.properties = {"test3": 6}
>>> print myobj.properties
{'test': 5, 'test2': 'string', 'test3': 6}
但是,正如你所看到的,这并不是完全符合预期的行为。我更希望的是这样的:
>>> myobj.properties['test3'] = 6
我尝试为 properties
添加一个 __setitem__
的定义:
class MyClass:
...
properties = property(get_properties)
properties.__setitem__ = set_property
但结果是这样的:
AttributeError: 'property' object has no attribute '__setitem__'
我试图让属性像字典一样,并简单地重写 __setitem__
和 __getitem__
,但它不接受。
你知道正确的方法是什么吗?我可以让类 property
像字典一样工作吗?
谢谢。
2 个回答
1
你定义的这个属性是只读的。不过,属性装饰器其实可以选择性地使用设置和获取函数:
class MyClass(object):
def __init__(self):
self.d = {"hello":None}
def __repr__(self):
return "%s"%(self.d,)
def get(self):
return self.d
def set(self, value):
self.d = value
x = property(get, set)
通过为你的内部字典定义一个设置器,你现在可以在字典上设置键值了:
>>> c = MyClass()
>>> c.x
{'hello': None}
>>> c.x["hello"] = "world"
>>> c.x
{'hello': 'world'}
>>> c.x = {"key":"value"}
>>> c.x
{'key': 'value'}
另外,如果你使用的是更新版本的Python(2.6及以上),你可以用装饰器以更简洁的方式来写:
class MyClass():
def __init__(self):
self.d = {"hello":None}
def __repr__(self):
return "%s"%(self.d,)
@property
def x(self):
return self.d
@x.setter
def set(self, value):
self.d = value
2
好的,Mike的回答让我想到一个解决办法,就是在属性的获取方法里返回一个扩展的字典类。在这个类里,我根据上下文重写了 __setitem__
方法:
class MyClass(object):
def get_properties():
... (call C function and convert to dict)
def set_property():
... (call C function)
def propgetter(self):
context = self
props = self.get_properties()
class propsetter(dict):
__getitem__ = props.__getitem__
def __setitem__(self, key, value):
props[key] = value
context.set_property(key, value)
return propsetter(self.get_properties())
properties = property(propgetter)
看起来效果正如我所希望的那样。