子类化Python字典以重写__setitem__方法

17 浏览
0 Comments

子类化Python字典以重写__setitem__方法

我正在构建一个继承自dict的类,并重写了__setitem__方法。我希望确保我的方法在所有可能设置字典项的实例中都会被调用。

我发现了三种情况,在这些情况下,Python(在这种情况下是2.6.4版本)在设置值时不会调用我的重写的__setitem__方法,而是直接调用PyDict_SetItem

  1. 在构造函数中
  2. setdefault方法中
  3. update方法中

作为一个非常简单的测试:

class MyDict(dict):
    def __setitem__(self, key, value):
        print "Here"
        super(MyDict, self).__setitem__(key, str(value).upper())
>>> a = MyDict(abc=123)
>>> a['def'] = 234
Here
>>> a.update({'ghi': 345})
>>> a.setdefault('jkl', 456)
456
>>> print a
{'jkl': 456, 'abc': 123, 'ghi': 345, 'def': '234'}

你可以看到,只有在显式设置项时才会调用重写的方法。为了让Python始终调用我的__setitem__方法,我不得不重新实现这三个方法,如下所示:

class MyUpdateDict(dict):
    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)
    def __setitem__(self, key, value):
        print "Here"
        super(MyUpdateDict, self).__setitem__(key, value)
    def update(self, *args, **kwargs):
        if args:
            if len(args) > 1:
                raise TypeError("update expected at most 1 arguments, got %d" % len(args))
            other = dict(args[0])
            for key in other:
                self[key] = other[key]
        for key in kwargs:
            self[key] = kwargs[key]
    def setdefault(self, key, value=None):
        if key not in self:
            self[key] = value
        return self[key]

还有其他需要重写的方法吗?以便知道Python将始终调用我的__setitem__方法吗?

更新

根据gs的建议,我尝试继承UserDict(实际上是IterableUserDict,因为我想遍历键)如下所示:

from UserDict import *;
class MyUserDict(IterableUserDict):
    def __init__(self, *args, **kwargs):
        UserDict.__init__(self,*args,**kwargs)
    def __setitem__(self, key, value):
        print "Here"
        UserDict.__setitem__(self,key, value)

这个类似乎在setdefault上正确调用了我的__setitem__,但它没有在update或在构造函数中提供初始数据时调用它。

更新2

Peter Hansen的建议让我更仔细地查看了dictobject.c,并意识到可以简化一下update方法,因为内置的字典构造函数只是简单地调用了内置的update方法。现在它看起来像这样:

def update(self, *args, **kwargs):
    if len(args) > 1:
        raise TypeError("update expected at most 1 arguments, got %d" % len(args))
    other = dict(*args, **kwargs)
    for key in other:
        self[key] = other[key]

0