Ответ 1
OrderedCounter приведен в качестве примера в документации OrderedDict и работает без необходимости переопределения любых методов:
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 можно найти в 2.3 Python документации.