python - 子类化 Python 字典以覆盖 __setitem__

标签 python dictionary subclass

我正在构建一个子类 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]

最佳答案

我正在回答我自己的问题,因为我最终决定我真的确实想要继承 Dict,而不是创建一个新的映射类,并且 UserDict 在某些情况下仍然遵循底层的 Dict 对象情况,而不是使用提供的 __setitem__

在阅读和重新阅读 Python 2.6.4 源代码(主要是 Objects/dictobject.c,但我在其他地方寻找各种方法的使用位置)之后,我的理解是以下代码 足以在每次更改对象时调用我的 __setitem__ ,否则其行为与 Python Dict 完全相同:

Peter Hansen 的建议让我更仔细地查看 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)

它通过了。我尝试过的所有其他实现在某些时候都失败了。我仍然会接受任何表明我错过了什么的答案,但除此之外,我会在几天后勾选这个旁边的复选标记,并将其称为正确答案:)

关于python - 子类化 Python 字典以覆盖 __setitem__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2060972/

相关文章:

python - 正整数数组,有效实现的想法

Python-从字符串中提取文本

Java:一个类可以同时继承两个父类(super class)吗?

Java不接受无符号字节,我正在尝试映射它们

python - 将文件读入字典python

windows - 如何子类化 Windows 资源管理器的窗口

iphone - 将 UITabBarController 内的 UITabBar 转换到我自己的类(class)

python - 为什么显示退出状态 1?

python - 嵌套的 for 循环,设置变量和 if else 在批处理脚本中

python - 具有多个键映射到相同值的字典