如何重写字典类对象的属性设置?

1 投票
2 回答
2098 浏览
提问于 2025-04-16 12:41

我正在写一个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)

看起来效果正如我所希望的那样。

撰写回答