在字典上使用@property装饰器

15 投票
2 回答
21607 浏览
提问于 2025-04-16 00:33

我正在尝试在一个类里面使用Python的@property装饰器来处理字典。我的想法是,我想要一个特定的值(我们叫它'message')在被访问后清空。但我还希望另一个值(我们叫它'last_message')能保存上一个设置的消息,并在设置新消息之前一直保持。按照我的想法,这段代码应该能正常工作:

>>> class A(object):
...     def __init__(self):
...             self._b = {"message": "", 
...                        "last_message": ""}
...     @property
...     def b(self):
...             b = self._b
...             self._b["message"] = ""
...             return b
...     @b.setter
...     def b(self, value):
...             self._b = value
...             self._b["last_message"] = value["message"]
...
>>>

但是,它似乎并没有按我预期的那样工作:

>>> a = A()
>>> a.b["message"] = "hello"
>>> a.b["message"]
''
>>> a.b["last_message"]
''
>>>

我不太确定我哪里做错了?我觉得@property在字典上似乎没有我想象中的那样运作,但也许我在其他地方根本就搞错了?

另外,我知道我可以在类里面使用单独的值。但这是在一个网页应用中实现的会话,我需要它是一个字典。我可以让这个工作,或者让整个会话对象假装成一个字典,或者使用单独的变量并在代码的其他部分进行一些黑科技处理。我更希望能让这个直接工作。

2 个回答

3

我可能没完全理解你想要做的事情,不过这样做能解决你的问题吗?

class A(object):
    def __init__(self):
        self._b = {'message':'',
                   'last_message': ''}

    @property
    def b(self):
        b = self._b.copy()
        self._b['message'] = ''
        return b

    @b.setter
    def b(self, value):
        self._b['message'] = value
        self._b['last_message'] = value


if __name__ == "__main__":
    a = A()
    a.b = "hello"
    print a.b
    print a.b
    print a.b["last_message"]

$ python dictPropTest.py
{'last_message': 'hello', 'message': 'hello'}
{'last_message': 'hello', 'message': ''}
hello
21
class MyDict(dict):
    def __setitem__(self, key, value):
        if key == 'message':
            super().__setitem__('message', '')
            super().__setitem__('last_message', value) 
        else:
            super().__setitem__(key, value)

class A(object):
    def __init__(self):
        self._b = MyDict({"message": "", 
                          "last_message": ""})

    @property
    def b(self):
        return self._b

a = A()
a.b['message'] = 'hello'
print(a.b['message'])
# ''
print(a.b['last_message'])
# hello

我想你已经发现了,导致你的设置器(setter)不工作的原因是因为

a.b['message']='hello'

首先访问了 a.b,这会调用 b 属性的获取器(getter),而不是设置器(setter)。获取器返回的是字典 self._b。然后 self._b['message']='hello' 这行代码会触发字典的 __setitem__ 方法。

所以,要解决这个问题,你需要一个特殊的字典(比如 MyDict)。

撰写回答