python python-3.x dictionary counter ordereddictionary

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.