We can create an OrderedCounter
trivially by using multiple inheritance:
>>> from collections import Counter, OrderedDict
>>> class OrderedCounter(Counter, OrderedDict):
... pass
...
>>> OrderedCounter('Mississippi').items()
[('M', 1), ('i', 4), ('s', 4), ('p', 2)]
Correct me if I'm wrong, but this crucially relies on the fact that Counter
uses super
:
class Counter(dict):
def __init__(*args, **kwds):
...
super(Counter, self).__init__()
...
That is, the magic trick works because
>>> OrderedCounter.__mro__
(__main__.OrderedCounter,
collections.Counter,
collections.OrderedDict,
dict,
object)
The super
call must delegate according to 'siblings before parents' rule of the mro, whence the custom class uses an OrderedDict
as the storage backend.
However a colleague recently pointed out, to my surprise, that OrderedDict
doesn't use super:
def __setitem__(self, key, value,
dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link):
...
# <some weird stuff to maintain the ordering here>
dict_setitem(self, key, value)
At first I thought it could be because OrderedDict
came first and Raymond didn't bother to change it later, but it seems that super
predates OrderedDict
.
Why does OrderedDict
call dict.__setitem__
explicitly?
And why does it need to be a kwarg? Doesn't this cause trouble when using OrderedDict
in diamond inheritance situations, since it passes directly to the parent class instead of delegating to the next in line in the mro?
It's a microoptimization. Looking up a
dict_setitem
argument is slightly faster than looking updict.__setitem__
orsuper().__setitem__
.This might cause problems with multiple inheritance if you have another class that overrides
__setitem__
, butOrderedDict
isn't designed for that kind of diamond-structured method overriding anyway. ForOrderedDict
to support that, it'd have to make very careful guarantees about what another class's methods might see if they try to index theOrderedDict
while the ordering information is inconsistent with the dict structure. Such guarantees would be way too messy to make.