recorrer - ordereddict python
¿Por qué los valores de un OrderedDict no son iguales? (3)
Con Python 3:
>>> from collections import OrderedDict
>>> d1 = OrderedDict([(''foo'', ''bar'')])
>>> d2 = OrderedDict([(''foo'', ''bar'')])
Quería verificar la igualdad:
>>> d1 == d2
True
>>> d1.keys() == d2.keys()
True
Pero:
>>> d1.values() == d2.values()
False
¿Sabes por qué los valores no son iguales?
He probado esto con Python 3.4 y 3.5.
Después de esta pregunta, publiqué en la lista de correo de Python-Ideas para obtener detalles adicionales:
https://mail.python.org/pipermail/python-ideas/2015-December/037472.html
Desafortunadamente, las dos respuestas actuales no abordan el motivo de esto, sino que se centran en cómo se hace esto. Esa discusión en la lista de correo fue increíble, así que resumiré las cosas:
Para odict.keys
/ dict.keys
y odict.items
/ dict.items
:
-
odict.keys
( subclase dedict.keys
) admite la comparación debido a su conformidad concollections.abc.Set
(es un objeto similar a un conjunto). Esto es posible debido al hecho de que laskeys
dentro de un diccionario (ordenadas o no) tienen la garantía de ser únicas y halables. odict.items
( subclase dedict.items
) también admite la comparación por el mismo motivo que.keys
.itemsview
está permitido hacer esto ya que genera el error apropiado si uno de lositem
(específicamente, el segundo elemento que representa el valor) no es hashable, sin embargo se garantiza la unicidad (debido a que laskeys
son únicas):>>> od = OrderedDict({''a'': []}) >>> set() & od.items() TypeErrorTraceback (most recent call last) <ipython-input-41-a5ec053d0eda> in <module>() ----> 1 set() & od.items() TypeError: unhashable type: ''list''
Para ambas
keys
vista,items
, la comparación usa una función simple llamadaall_contained_in
(bastante legible) que usa el método de objetos__contain__
para verificar la membresía de los elementos en las vistas involucradas.
Ahora, sobre odict.values
/ dict.values
:
Como se ha notado,
odict.values
( subclase dedict.values
[shocker]) no se compara como un objeto similar a un conjunto. Esto se debe a que losvalues
de unavaluesview
devalues
no se pueden representar como un conjunto, las razones son dos:- Lo más importante es que la vista puede contener duplicados que no se pueden descartar.
- La vista puede contener objetos no procesables (que, por sí solo, no son suficientes para no tratar la vista como un conjunto).
Como se indica en un comentario de user2357112 y de @abarnett en la lista de correo, odict.values
/ dict.values
es un conjunto múltiple, una generalización de conjuntos que permite varias instancias de sus elementos. Intentar compararlos no es tan trivial como comparar keys
o items
debido a la duplicación inherente, el orden y el hecho de que probablemente deba tener en cuenta las claves que corresponden a esos valores. Deberían dict_values
que se ven así:
>>> {1:1, 2:1, 3:2}.values()
dict_values([1, 1, 2])
>>> {1:1, 2:1, 10:2}.values()
dict_values([1, 1, 2])
en realidad ser igual a pesar de que los valores que corresponden a las teclas no son lo mismo? ¿Tal vez? ¿Tal vez no? No es directo de ninguna manera y conducirá a una confusión inevitable.
Sin embargo, lo importante es que no es trivial compararlos como lo está con las keys
y los items
, para resumir, con otro comentario de @abarnett en la lista de correo :
Si está pensando que podríamos definir qué deberían hacer los multisectos, a pesar de no tener un tipo de conjunto múltiple estándar o un ABC para ellos, y aplicar eso a las vistas de valores, la siguiente pregunta es cómo hacer eso en un tiempo mejor que cuadrático para los no fiables valores. (Y tampoco puede suponer que se ordene aquí.) ¿Sería una mejora tener una vista de valores durante 30 segundos y luego regresar con la respuesta que intuitivamente quería en lugar de dar la respuesta incorrecta en 20 milisegundos? (De cualquier forma, vas a aprender la misma lección: no compares las vistas de valores. Prefiero aprender eso en 20 milisegundos).
En Python 3, dict.keys()
y dict.values()
devuelven clases iterables especiales, respectivamente a collections.abc.KeysView
y collections.abc.ValuesView
. El primero hereda su método __eq__
del set
, el segundo usa el object.__eq__
predeterminado object.__eq__
que prueba la identidad del objeto.
En python3, d1.values()
y d2.values()
son objetos collections.abc.ValuesView
:
>>> d1.values()
ValuesView(OrderedDict([(''foo'', ''bar'')]))
No los compare como un objeto, cámbielos a listas y luego compárelos:
>>> list(d1.values()) == list(d2.values())
True
Investigando por qué funciona para comparar claves, en _collections_abc.py
de CPython, KeysView
hereda de Set
mientras ValuesView
no:
class KeysView(MappingView, Set):
class ValuesView(MappingView):
Seguimiento de
__eq__
enValuesView
y sus padres:MappingView ==> Sized ==> ABCMeta ==> type ==> object
.__eq__
se implementa solo en elobject
y no se reemplaza.Por otro lado,
KeysView
hereda__eq__
directamente deSet
.