我首次尝试在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
提供的其他方法来实现它的答案。使用多重继承会更清晰。为什么它不起作用?
答案 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)