我一直在阅读super()
的工作原理。我遇到了this recipe,它演示了如何创建一个有序计数器:
from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first seen'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__,
OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
例如:
oc = OrderedCounter('adddddbracadabra')
print(oc)
OrderedCounter(OrderedDict([('a', 5), ('d', 6), ('b', 2), ('r', 2), ('c', 1)]))
有人能够解释这是如何神奇地起作用的吗?
这也出现在Python documentation。
OrderedCounter在OrderedDict documentation中作为示例给出,无需覆盖任何方法即可工作:
class OrderedCounter(Counter, OrderedDict):
pass
调用类方法时,Python必须找到要执行的正确方法。有一个定义的顺序,它在其中搜索称为“方法解析顺序”或mro的类层次结构。 mro存储在属性__mro__
中:
OrderedCounter.__mro__
(<class '__main__.OrderedCounter'>, <class 'collections.Counter'>, <class 'collections.OrderedDict'>, <class 'dict'>, <class 'object'>)
当OrderedDict的一个实例调用__setitem__()
时,它按顺序搜索类:OrderedCounter
,Counter
,OrderedDict
(找到它的地方)。所以像oc['a'] = 0
这样的声明最终会调用OrderedDict.__setitem__()
。
相比之下,__getitem__
没有被mro中的任何子类覆盖,所以count = oc['a']
由dict.__getitem__()
处理。
oc = OrderedCounter()
oc['a'] = 1 # this call uses OrderedDict.__setitem__
count = oc['a'] # this call uses dict.__getitem__
对于像oc.update('foobar').
这样的语句,会出现一个更有趣的调用序列。首先,调用Counter.update()
。 Counter.update()
的代码使用self [elem],后者变成了对OrderedDict.__setitem__()
的调用。而那个代码叫dict.__setitem__()
。
如果基类反转,则不再有效。因为mro是不同的,并且调用了错误的方法。
class OrderedCounter(OrderedDict, Counter): # <<<== doesn't work
pass
有关mro的更多信息可以在Python 2.3 documentation中找到。