如何改变Python字典的__setattr__行为?

5 投票
4 回答
6305 浏览
提问于 2025-04-17 09:38

在Python中,所有东西都有一个类。因此,dict(字典)也有自己的类。

所以,从理论上讲,我应该能够改变字典中键值对赋值的行为。


举个例子:

d = dict()
d['first'] = 3    # Internally d['first'] is stored as 6 [i.e. value*2 if value is INT]

print d['first']  # should print 6

d['second'] = 4 

print d['second'] # should print 8


我注意到大多数对象都有属性,可以通过 OBJECT.__dict__vars(OBJECT) 来查看。但是,dict(字典)和 list(列表)却不是这样。

我该如何通过重写 dict.__setattr__() 方法来实现我想要的行为呢?

4 个回答

2

你不能这样做。dict类是用C语言写的,是Python自带的一个功能,这意味着你不应该对它进行任何修改。

不过,你可以从这个类继承,创建一个自己的子类,并且可以在这个子类中重写你想要的方法。

8

在继承 dict 时要小心。如果你只是重写了 __setitem__ 方法,那么其他的 dict 方法,比如 update,就不会调用你重写的 __setitem__ 方法。

class MyDict(dict):
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)

d = MyDict()
d['first'] = 3
print(d['first'])
# 6

d.update({'first':4})
print(d['first'])
# 4                       # <--- __setitem__ was not called.

如果你想创建一个类似字典的对象,你要么需要继承 dict 并重写所有的方法(可以参考 OrderedDict 的例子),要么可以继承 collections.MutableMapping 并只重写其中的一小部分方法(其他的方法都是从这些方法派生出来的)。

import collections

class MyDict2(collections.MutableMapping,dict):
    def __getitem__(self, key):
        return dict.__getitem__(self, key)
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)
    def __delitem__(self, key):
        dict.__delitem__(self, key)
    def __iter__(self):
        return dict.__iter__(self)
    def __len__(self):
        return dict.__len__(self)
    def __contains__(self, x):
        return dict.__contains__(self, x)

d = MyDict2()
d['first'] = 3
print(d['first'])
# 6
d.update({'first':4})
print(d['first'])
# 8
12

在这种情况下,必须重写的是 __setitem__,这其实很简单:

class MyDict(dict):
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)

举个例子:

>>> m  = MyDict()
>>> m[0] = 5
>>> m
{0: 10}

__setattr__ 控制的是对象的属性是如何被设置的(不是键值对)。

撰写回答