我正在建立一个子类dict并重写的类__setitem__。我想确定在可能设置字典项的所有实例中都会调用我的方法。
dict
__setitem__
我发现了三种情况,其中Python(在本例中为2.6.4)__setitem__在设置值时未调用我的重写方法,而是PyDict_SetItem直接调用
PyDict_SetItem
setdefault
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)
此类似乎正确调用了我的__setitem__on setdefault,但并未将其调用on update,或者在向构造函数提供初始数据时未调用它。
更新2
彼得·汉森(Peter Hansen)的建议使我更加仔细地研究dictobject.c,并且我意识到可以对更新方法进行一些简化,因为内置字典构造函数无论如何都只是调用内置更新方法。现在看起来像这样:
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]
我正在回答我自己的问题,因为我最终决定我确实 确实 想对Dict进行子类化,而不是创建一个新的映射类,并且UserDict在某些情况下仍然遵循基础的Dict对象,而不是使用提供的__setitem__。
阅读和重新阅读了Python 2.6.4源之后(主要是Objects/dictobject.c,但我grepped eveywhere别的,看看那里的各种方法的使用,)我的理解是,下面的代码 是 足以让我的__setitem__叫每一个对象是时间更改,并以其他方式完全像Python Dict:
Objects/dictobject.c
彼得·汉森(Peter Hansen)的建议让我更加仔细地研究dictobject.c,并且我意识到原始答案中的update方法可以稍微简化一点,因为内置字典构造函数无论如何都只是调用内置update方法。因此,我的回答中的第二次更新已添加到下面的代码中(由一些乐于助人的人;-)。
dictobject.c
class MyUpdateDict(dict): def __init__(self, *args, **kwargs): self.update(*args, **kwargs) def __setitem__(self, key, value): # optional processing 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]
我已经用以下代码对其进行了测试:
def test_updates(dictish): dictish['abc'] = 123 dictish.update({'def': 234}) dictish.update(red=1, blue=2) dictish.update([('orange', 3), ('green',4)]) dictish.update({'hello': 'kitty'}, black='white') dictish.update({'yellow': 5}, yellow=6) dictish.setdefault('brown',7) dictish.setdefault('pink') try: dictish.update({'gold': 8}, [('purple', 9)], silver=10) except TypeError: pass else: raise RunTimeException("Error did not occur as planned") python_dict = dict([('b',2),('c',3)],a=1) test_updates(python_dict) my_dict = MyUpdateDict([('b',2),('c',3)],a=1) test_updates(my_dict)
它通过了。我尝试过的所有其他实现在某些时候都失败了。我仍然会接受任何表明我错过了某些东西的答案,但是否则,我将在几天之内在其旁边打勾,并称其为正确的答案:)