¿Por qué los valores de OrderedDict no son iguales?

Con Python 3:

>>> from collections import OrderedDict >>> d1 = OrderedDict([('foo', 'bar')]) >>> d2 = OrderedDict([('foo', 'bar')]) 

Quería comprobar 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.


Luego 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

En Python 3, dict.keys() y dict.values() devuelven clases iterables especiales, respectivamente, 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 collections.abc.ValuesView objetos:

 >>> d1.values() ValuesView(OrderedDict([('foo', 'bar')])) 

No los compare como un objeto, conviértalos en listas y luego compárelos:

 >>> list(d1.values()) == list(d2.values()) True 

Al investigar por qué funciona para comparar claves, en _collections_abc.py de CPython, KeysView está heredando de Set while ValuesView no:

 class KeysView(MappingView, Set): class ValuesView(MappingView): 
  • Rastreo de __eq__ en ValuesView y sus padres:

    MappingView ==> Sized ==> ABCMeta ==> type ==> object .

    __eq__ se implementa solo en el object y no se invalida.

  • Por otro lado, KeysView hereda __eq__ directamente de Set .

Desafortunadamente, las dos respuestas actuales no abordan por qué esto es así, sino que se centran en cómo se hace esto. La discusión de 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 de dict.keys ) admite la comparación debido a su conformidad con collections.abc.Set (es un objeto similar a un conjunto). Esto es posible debido a que se garantiza que las keys dentro de un diccionario (ordenadas o no) son únicas y hashable.
  • odict.items ( subclase de dict.items ) también admite la comparación por el mismo motivo que .keys hace .keys . está permitido hacer la vista de elementos ya que genera el error apropiado si uno de los item s (específicamente, el segundo elemento que representa el valor) no es hashable, aunque la unicidad está garantizada (debido a que las keys son únicas):

     >>> od = OrderedDict({'a': []}) >>> set() & od.items() TypeErrorTraceback (most recent call last)  in () ----> 1 set() & od.items() TypeError: unhashable type: 'list' 

    Para ambas keys vista, items , la comparación usa una función simple llamada all_contained_in (bastante legible) que usa el método de objetos __contain__ para verificar la pertenencia de los elementos en las vistas involucradas.

Ahora, sobre odict.values / dict.values :

  • Como se notó, los odict.values ( subclase de dict.values [shocker]) no se comparan como un objeto similar a un conjunto. Esto se debe a que los values de una valuesview de values no se pueden representar como un conjunto, las razones son dobles:

    1. Lo más importante es que la vista puede contener duplicados que no se pueden eliminar.
    2. La vista puede contener objetos no hashable (que, por sí mismos, no es suficiente para no tratar la vista como un conjunto).

Como se indica en un comentario de @ user2357112 y @abarnett en la lista de correo, odict.values / dict.values es un conjunto múltiple, una generalización de conjuntos que permite múltiples instancias de sus elementos. Tratar de compararlos no es tan trivial como comparar keys o items debido a la duplicación inherente, al orden y al hecho de que probablemente deba tener en cuenta las claves que corresponden a esos valores. Deben 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 aunque los valores que corresponden a las claves no sean los mismos? ¿Tal vez? ¿Tal vez no? No es sencillo de ninguna manera y dará lugar a una confusión inevitable.

Sin embargo, lo que se debe hacer es que no es trivial compararlos 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 conjuntos múltiples, 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 hacerlo en un tiempo mejor que el cuadrático para no-hashable. valores. (Y tampoco puede asumir el orden aquí). ¿Una visión de valores se mantendría durante 30 segundos y luego regresaría con la respuesta que buscaba de manera intuitiva en lugar de dar una respuesta incorrecta en 20 milis una mejora? (De cualquier manera, aprenderá la misma lección: no compare las vistas de valores. Prefiero aprender eso en 20 milis.)