开发者

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜