为什么我不能通过继承OrderedDict和defaultdict来创建默认的,有序的dict?

时间:2014-12-30 20:39:00

标签: python python-3.x multiple-inheritance defaultdict ordereddictionary

我首次尝试在collections模块中组合两个词典的功能是创建一个继承它们的类:

from collections import OrderedDict, defaultdict

class DefaultOrderedDict(defaultdict, OrderedDict):
    def __init__(self, default_factory=None, *a, **kw):
        super().__init__(default_factory, *a, **kw)

但是,我无法将项目分配到此词典:

d = DefaultOrderedDict(lambda: 0)
d['a'] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.3/collections/__init__.py", line 64, in __setitem__
    self.__map[key] = link = Link()
AttributeError: 'DefaultOrderedDict' object has no attribute '_OrderedDict__map'

事实上,this question about how to create a similar object有通过扩展OrderedDict类并手动重新实现defaultdict提供的其他方法来实现它的答案。使用多重继承会更清晰。为什么它不起作用?

2 个答案:

答案 0 :(得分:5)

原因是init method of defaultdict而不是调用MRO中下一个类的__init__调用PyDict_Type的init,因此设置了__map之类的一些属性OrderedDict's __init__永远不会被初始化,因此错误。

>>> DefaultOrderedDict.mro()
[<class '__main__.DefaultOrderedDict'>,
 <class 'collections.defaultdict'>,
 <class 'collections.OrderedDict'>,
 <class 'dict'>, <class 'object'>]

并且defaultdict没有自己的__setitem__方法:

>>> defaultdict.__setitem__
<slot wrapper '__setitem__' of 'dict' objects>
>>> dict.__setitem__
<slot wrapper '__setitem__' of 'dict' objects>
>>> OrderedDict.__setitem__
<unbound method OrderedDict.__setitem__>

因此,当您调用d['a'] = 1时,搜索__setitem__ Python到达OrdereredDict的__setitem__并且他们对未初始化的__map属性的访问引发了错误:


修复方法是明确调用__init__defaultdict上的OrderedDict

class DefaultOrderedDict(defaultdict, OrderedDict):
    def __init__(self, default_factory=None, *a, **kw):
        for cls in DefaultOrderedDict.mro()[1:-2]:
            cls.__init__(self, *a, **kw)

答案 1 :(得分:4)

也许你是来自Java背景,但是多重继承并不能完成你在Python中所期望的那样。从defaultOrderedDict的 init 调用super会将super()调用为defaultdict的 init ,而不会调用OrderedDict的 init map属性首先在OrderedDict的__init 函数中定义。实现如下(来自源代码):

def __init__(self, *args, **kwds):
    '''Initialize an ordered dictionary.  The signature is the same as
    regular dictionaries, but keyword arguments are not recommended because
    their insertion order is arbitrary.

    '''
    if len(args) > 1:
        raise TypeError('expected at most 1 arguments, got %d' % len(args))
    try:
        self.__root
    except AttributeError:
        self.__root = root = []                     # sentinel node
        root[:] = [root, root, None]
        self.__map = {}
    self.__update(*args, **kwds)

请注意,这与私有属性无关。具有多重继承的最小示例可以说明这一点:

class Foo:
    def __init__(self):
        self.foo=2

class Bar:
    def __init__(self):
        self.bar=1

class FooBar(Foo,Bar):
     def __init__(self):
        super().__init__()

fb = FooBar()

fb.foo
>>2 
fb.bar
>>AttributeError: 'FooBar' object has no attribute 'bar'

所以,Bar的构造函数从未被调用过。 Pythons方法解析顺序从左到右,直到找到一个具有它所寻找的函数名的类(在本例中为 init ),然后忽略右边的所有其他类(在本例中为Bar)