ordereddict dict python python-3.x dictionary ipython python-3.6

python ordereddict to dict



¿Cómo puede OrderedDict conocer el orden de los elementos de un dict ya instanciado? (3)

Estaba jugando con el tipo OrderedDict en Python 3.6 y me sorprendió su comportamiento. Cuando creo un dict simple como este en IPython:

d = dict([(''sape'', 4139), (''guido'', 4127), (''jack'', 4098)])

Yo obtengo:

{''guido'': 4127, ''jack'': 4098, ''sape'': 4139}

como una salida, que no conserva el orden de los elementos en la instanciación por alguna razón. Ahora, cuando creo un OrderedDict de d como este:

od = OrderedDict(d)

la salida es:

OrderedDict([(''sape'', 4139), (''guido'', 4127), (''jack'', 4098)])

Ahora me pregunto, ¿cómo puede saber el OrderedDict sobre el orden de los elementos en la instanciación de d ? ¿Y siempre se comporta igual, de modo que puedo confiar en el orden de los elementos en el OrderedDict ?

Ya estaba leyendo la documentación de Python sobre diccionarios y OrderedDict s, pero no encontré una respuesta a mi pregunta.

La salida de ( sys.version ):

In[22]: sys.version Out[22]: ''3.6.1 (default, Apr 4 2017, 09:40:21) /n[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)]''


Ahora es obvio que el gancho personalizado ( sys.displayhook ) que utiliza IPython para mostrar la salida es imprimir cosas bonitas ( usar su propia impresora bonita ).

Al llamar directamente a displayhook , puede ver cómo arruina el orden de inserción:

In [1]: from sys import displayhook ...: displayhook({''1'': 0, ''0'': 1}) Out[1]: {''0'': 1, ''1'': 0}

Además, si tomas la str diccionario (enviando una cadena para que se muestre en lugar de un objeto dict) obtendrías el orden correcto y esperado:

In [2]: d = dict([(''sape'', 4139), (''guido'', 4127), (''jack'', 4098)]) ...: d Out[2]: {''guido'': 4127, ''jack'': 4098, ''sape'': 4139} In [3]: str(dict(t)) Out[3]: "{''sape'': 4139, ''guido'': 4127, ''jack'': 4098}"

de manera similar print .

No estoy seguro de por qué IPython hace esto con 3.6 , fue bastante confuso (edición: vea el tema relevante en GitHub ). En su REPL estándar de Python, este comportamiento no se manifestará ya que sys.displayhook no está implementado para realizar una impresión bonita.

El dictado que ha creado mantiene el orden de inserción, por eso OrderedDict mantiene ese mismo orden.

El hecho de que lo haga es, por supuesto, un detalle de implementación. Hasta que se cambie (y parece que lo hará), debe seguir usando OrderedDict para mantener de manera confiable el orden en todas las implementaciones.

Por cierto, si quieres deshabilitarlo, puedes iniciar IPython con la --no-pprint que desactiva su impresora bonita:

➜ ipython --no-banner --no-pprint In [1]: dict([(''sape'', 4139), (''guido'', 4127), (''jack'', 4098)]) Out[1]: {''sape'': 4139, ''guido'': 4127, ''jack'': 4098}


Como probablemente sepas, los diccionarios en Python no están ordenados de acuerdo con la especificación del lenguaje. Tienen un orden inherente, pero ese orden es arbitrario.

Entonces, cuando pasa un diccionario estándar al constructor de un OrderedDict , el nuevo OrderedDict se llenará a partir de los valores del diccionario original mediante la iteración de sus valores. De esa manera, se utilizará el orden inherente del diccionario, y eso será lo que verá en el OrderedDict final.

Ahora, con Python 3.6, hubo un cambio en la implementación del diccionario predeterminado. Como se discutió y explicó en esta pregunta , los diccionarios estándar ahora conservan el orden de inserción. Es por eso que cuando creó OrderedDict desde el OrderedDict de Python 3.6, el orden original también se conserva.

¿Significa esto que OrderedDict vuelve obsoleto en Python 3.6+? No, ya que la conservación del orden de los diccionarios estándar es un detalle de la implementación . En lugar del orden arbitrario de implementaciones anteriores, el nuevo diccionario simplemente tiene el orden "correcto". Pero esto no está garantizado de ninguna manera por la especificación del lenguaje, y puede o no ser el caso para otras implementaciones. Como tal, no puedes y no debes confiar en ello.

Por cierto tenga en cuenta que Python 3.6 (el lenguaje, no solo la implementación) garantiza que se OrderedDict el orden de los argumentos de las palabras clave en OrderedDict . Por ejemplo, esto conserva el orden:

>>> OrderedDict(sape=4139, guido=4127, jack=4098) OrderedDict([(''sape'', 4139), (''guido'', 4127), (''jack'', 4098)])


En 3.6, como detalle de implementación, todos los dict están ordenados. IPython te está engañando: Antes de 3.6, el orden de las teclas era arbitrario, por lo que, para facilitar el uso, la salida interactiva de IPython para dict y set (donde Python normal simplemente repr la repr ) ordena las teclas. Es por eso que su dict parece estar en orden alfabético. Es posible que IPython finalmente elimine ese comportamiento cuando se ejecuta en la versión 3.6+, ya que, como ha notado, es bastante confuso.

Si print explícita, en lugar de confiar en ipython para generar los resultados de la expresión anterior, ipython la magia REPL de ipython y verá el orden "natural". Lo mismo ocurre con cualquier otro medio de interacción con el dict , ya que la iteración se realizará en el orden esperado.