代码之家  ›  专栏  ›  技术社区  ›  Ian Clelland

对python字典进行子类化以重写\ setitem__

  •  37
  • Ian Clelland  · 技术社区  · 15 年前

    我正在构建一个类 dict 和超越 __setitem__ . 我想确定在可能设置字典项的所有实例中都将调用我的方法。

    我发现了三种情况,其中python(在本例中为2.6.4)不调用我的overrided 第七节 方法设置值,而不是调用 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总是调用我的 第七节 方法,我必须重新实现这三种方法,如:

    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 总是 打电话给我 第七节 方法?

    更新

    根据GS的建议,我尝试将userdict子类化(实际上是iterableueserdict,因为我想遍历键),如下所示:

    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)
    

    这门课似乎正确地称我为 第七节 设置默认值 但是它没有启动 更新 或者当初始数据提供给构造函数时。

    更新2

    彼得·汉森的建议让我更仔细地研究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]
    
    4 回复  |  直到 10 年前
        1
  •  46
  •   martineau    12 年前

    我在回答我自己的问题,因为我最终决定 希望对dict进行子类化,而不是创建新的映射类,并且在某些情况下,userdict仍然遵从基础dict对象,而不是使用提供的 __setitem__ .

    在读取和重新读取python 2.6.4源代码之后(主要是 Objects/dictobject.c ,但我在其他地方做了大量的研究,以了解在哪里使用各种方法,)我的理解是以下代码 足以让每次更改对象时都调用我的uu setitem_uuuuuuuuuuuu,否则行为完全像python dict:

    彼得·汉森的建议让我更仔细地观察 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)
    

    它过去了。我尝试过的所有其他实现在某个时刻都失败了。我仍然会接受任何能表明我错过了什么的答案,但否则,我会在几天内勾选这一个旁边的复选标记,并称之为正确的答案:)

        2
  •  4
  •   mluebke    15 年前

    您对dict子类化的用例是什么?

    您不需要这样做来实现类似dict的对象,在您的情况下,编写一个普通类,然后添加对dict接口所需子集的支持可能会更简单。

    实现您所追求的目标的最佳方法可能是mutablemapping抽象基类。 PEP 3119 -- Introducing Abstract Base Classes

    这也将帮助您回答“是否还有其他方法需要重写?”。您将需要重写所有抽象方法。对于可变映射:抽象方法包括 设置项 , 德莱特 . 具体方法包括pop、popitem、clear、update。

        3
  •  3
  •   rizac    10 年前

    我发现伊恩的回答和评论非常有帮助和清晰。我只想指出,也许是第一次给超级班打电话 __init__ 如果不需要,方法可能更安全:我最近需要实现一个自定义 OrderedDict (我正在使用python 2.7):根据建议的 MyUpdateDict 实现时,我发现通过简单地替换

    class MyUpdateDict(dict):
    

    用:

    from collections import OrderedDict
    class MyUpdateDict(OrderedDict):
    

    然后上面发布的测试代码失败:

    Traceback (most recent call last):
    File "Desktop/test_updates.py", line 52, in <module>
        my_dict = MyUpdateDict([('b',2),('c',3)],a=1)
    File "Desktop/test_updates.py", line 5, in __init__
        self.update(*args, **kwargs)
    File "Desktop/test_updates.py", line 18, in update
        self[key] = other[key]
    File "Desktop/test_updates.py", line 9, in __setitem__
        super(MyUpdateDict, self).__setitem__(key, value)
    File "/usr/lib/python2.7/collections.py", line 59, in __setitem__
        root = self.__root
    AttributeError: 'MyUpdateDict' object has no attribute '_OrderedDict__root'
    

    看着 collections.py code 结果是有秩序的信息技术 需要 它的 爱因斯坦 方法以初始化和设置必要的自定义属性。

    因此,只需向super添加第一个调用 _初始化__ 方法,

    from collections import OrderedDict
    class MyUpdateDict(Orderedict):
    def __init__(self, *args, **kwargs):
        super(MyUpdateDict, self).__init__() #<-- HERE call to super __init__
        self.update(*args, **kwargs)
    

    我们有一个更通用的解决方案,显然对dict和ordereddict都有效。

    我不能说明这个解决方案是否一般有效,因为我只使用ordereddict测试它。但是,很可能是打电话给超级 爱因斯坦 当试图扩展其他dict子类时,方法不是无害的,就是必要的,而不是有害的。

        4
  •  0
  •   Shahul Hameed P    12 年前

    使用object.keyname=value而不是object[“keyname”]=value