python - Crear un contador ordenado
python-3.x dictionary (1)
He estado leyendo sobre cómo funciona
super()
.
Encontré
esta receta
que demuestra cómo crear un contador ordenado:
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),)
Por ejemplo:
oc = OrderedCounter(''adddddbracadabra'')
print(oc)
OrderedCounter(OrderedDict([(''a'', 5), (''d'', 6), (''b'', 2), (''r'', 2), (''c'', 1)]))
¿Alguien puede explicar cómo funciona esto mágicamente?
Esto también aparece en la documentación de Python .
OrderedCounter se proporciona como ejemplo en la documentación de OrderedDict y funciona sin necesidad de anular ningún método:
class OrderedCounter(Counter, OrderedDict):
pass
Cuando se llama a un método de clase, Python tiene que encontrar el método correcto para ejecutar.
Hay un orden definido en el que busca en la jerarquía de clases llamada "orden de resolución de método" o mro.
El mro se almacena en el atributo
__mro__
:
OrderedCounter.__mro__
(<class ''__main__.OrderedCounter''>, <class ''collections.Counter''>, <class ''collections.OrderedDict''>, <class ''dict''>, <class ''object''>)
Cuando una instancia de un OrderedDict llama a
__setitem__()
, busca las clases en orden:
OrderedCounter
,
Counter
,
OrderedDict
(donde se encuentra).
Entonces, una declaración como
oc[''a''] = 0
termina llamando a
OrderedDict.__setitem__()
.
Por el contrario,
__getitem__
no está anulado por ninguna de las subclases en el mro, por lo que
count = oc[''a'']
es manejado por
dict.__getitem__()
.
oc = OrderedCounter()
oc[''a''] = 1 # this call uses OrderedDict.__setitem__
count = oc[''a''] # this call uses dict.__getitem__
Se produce una secuencia de llamada más interesante para una instrucción como
oc.update(''foobar'').
Primero, se llama a
Counter.update()
.
El código para
Counter.update()
usa self [elem], que se convierte en una llamada a
OrderedDict.__setitem__()
.
Y el código para
eso
llama a
dict.__setitem__()
.
Si las clases base se invierten, ya no funciona. Porque el mro es diferente y se llama a los métodos incorrectos.
class OrderedCounter(OrderedDict, Counter): # <<<== doesn''t work
pass
Se puede encontrar más información sobre mro en la documentation Python 2.3.