[英]Why can't I create a default, ordered dict by inheriting OrderedDict and defaultdict?
My first attempt to combine the features of two dictionaries in the collections
module was to create a class that inherits them: 我第一次尝试在
collections
模块中组合两个字典的功能是创建一个继承它们的类:
from collections import OrderedDict, defaultdict
class DefaultOrderedDict(defaultdict, OrderedDict):
def __init__(self, default_factory=None, *a, **kw):
super().__init__(default_factory, *a, **kw)
However, I cannot assign an item to this dictionary: 但是,我无法为此词典分配项目:
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'
Indeed, this question about how to create a similar object has answers that achieve it by extending the OrderedDict
class and manually re-implementing the additional methods provided defaultdict
. 实际上, 关于如何创建类似对象的这个问题有通过扩展
OrderedDict
类并手动重新实现defaultdict
提供的其他方法来实现它的答案。 Using multiple inheritance would be cleaner. 使用多重继承会更清晰。 Why doesn't it work?
为什么不起作用?
The reason is that the init
method of defaultdict
instead of calling __init__
of the next class in MRO calls init of PyDict_Type
hence some of the attributes like __map
that are set in OrderedDict's __init__
are never initialized, hence the error. 原因是
defaultdict
的init
方法而不是调用MRO中下一个类的__init__
调用了PyDict_Type
init,因此在OrderedDict的__init__
中设置的一些属性如__map
从未被初始化,因此错误。
>>> DefaultOrderedDict.mro()
[<class '__main__.DefaultOrderedDict'>,
<class 'collections.defaultdict'>,
<class 'collections.OrderedDict'>,
<class 'dict'>, <class 'object'>]
And defaultdict
don't have their own __setitem__
method: 而且
defaultdict
没有自己的__setitem__
方法:
>>> defaultdict.__setitem__
<slot wrapper '__setitem__' of 'dict' objects>
>>> dict.__setitem__
<slot wrapper '__setitem__' of 'dict' objects>
>>> OrderedDict.__setitem__
<unbound method OrderedDict.__setitem__>
So, when you called d['a']
= 1, in search of __setitem__
Python reached OrdereredDict's __setitem__
and their the access of uninitialized __map
attribute raised the error: 所以,当你调用
d['a']
= 1时,搜索__setitem__
Python到达OrdereredDict的__setitem__
并且他们对未初始化的__map
属性的访问引发了错误:
A fix will be to call __init__
on both defaultdict
and OrderedDict
explicitly: 修复将显式调用
defaultdict
和OrderedDict
上的__init__
:
class DefaultOrderedDict(defaultdict, OrderedDict):
def __init__(self, default_factory=None, *a, **kw):
for cls in DefaultOrderedDict.mro()[1:-2]:
cls.__init__(self, *a, **kw)
Perhaps you are coming from a Java background, but multiple inheritance doesn't do what you'd expect it does in Python. 也许你来自Java背景,但是多重继承并不能达到你在Python中所期望的那样。 Calling super from the init of the defaultOrderedDict calls the super() as the init of defaultdict and never the init of OrderedDict.
从defaultOrderedDict的初始化调用超级调用超()作为defaultdict的init和OrderedDict从未初始化 。 The map attribute is first defined in the __init function of OrderedDict.
map属性首先在 OrderedDict 的__init函数中定义 。 The implementation is the following (from source):
实现如下(来自源代码):
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)
Note that this doesn't have to do with the attribute being private. 请注意,这与私有属性无关。 A minimal example with multiple inheritance can illustrate this:
具有多重继承的最小示例可以说明这一点:
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'
So, the constructor of Bar was never called. 因此,Bar的构造函数从未被调用过。 Pythons method resolution order goes from left to right until it finds a class with the function name it seeks (in this case init ) and then ignores all other classes on the right (in this case Bar)
Pythons方法解析顺序从左到右,直到找到一个具有它所寻找的函数名的类(在本例中为init ),然后忽略右边的所有其他类(在本例中为Bar)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.