How can I override property setting for a dict-like object?
I'm writing a C binding, and the C structure that I'm wrapping has some string-indexed property values.
I'd like to expose these as a dict in python.
So far, I've got a function get_properties
, which returns all the object's properties as a single dict. I wrapped this using the property
function in the class definition, so that I can access it as a class attribute:
(in class definition)
class MyClass:
def get_properties(self):
...
properties = property(get_properties)
(example)
>>> print myobj.properties
{'test': 5, 'test2': 'string'}
Now, I'd like to handle setting them in a dict-like way. I have a wrapper function for the C function called set_property
, which takes a string key and a value of several types.
I tried using set_properties
from my class property
:
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)
This works like this following:
>>> myobj.properties = {"test3": 6}
>>> print myobj.properties
{'test': 开发者_运维知识库5, 'test2': 'string', 'test3': 6}
However, as you can see it's not entirely the expected behaviour. What I'd prefer is something like:
>>> myobj.properties['test3'] = 6
I tried adding a definition for __setitem__
to properties
:
class MyClass:
...
properties = property(get_properties)
properties.__setitem__ = set_property
But this got me,
AttributeError: 'property' object has no attribute '__setitem__'
I tried to make property a dict and simply override __setitem__
and __getitem__
but it wouldn't have it.
Any idea what the correct way to do this is? Can I make a class property
behave like a dictionary?
Thanks.
Okay, Mike's answer gave me the idea to solve this by returning from the property's getter with an extended dict class, in which I override __setitem__
based on the context:
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)
Seems to work as I want.
The way you've defined your property it is read only. However, the property decorator actually optionally takes set and get functions:
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)
By defining a setter for your internal dictionary you can now set keys on it:
>>> c = MyClass()
>>> c.x
{'hello': None}
>>> c.x["hello"] = "world"
>>> c.x
{'hello': 'world'}
>>> c.x = {"key":"value"}
>>> c.x
{'key': 'value'}
Also, if you're using a more recent version of Python (2.6+) you could write this a nicer way with decorators like this:
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
精彩评论