¿Cómo mantener las claves / valores en el mismo orden en que se declararon?

320

Tengo un diccionario que he declarado en un orden particular y quiero mantenerlo en ese orden todo el tiempo. Las claves / valores realmente no pueden mantenerse en orden en función de su valor, solo lo quiero en el orden en que lo declaró.

Entonces si tengo el diccionario:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

No está en ese orden si lo veo o repito, ¿hay alguna manera de asegurarme de que Python mantenga el orden explícito en el que declaró las claves / valores?

roflwaffle
fuente

Respuestas:

229

Desde Python 3.6 en adelante, el dicttipo estándar mantiene el orden de inserción por defecto.

Definiendo

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

dará como resultado un diccionario con las claves en el orden indicado en el código fuente.

Esto se logró mediante el uso de una matriz simple con enteros para la tabla hash dispersa, donde esos enteros se indexan en otra matriz que almacena los pares clave-valor (más el hash calculado). Esa última matriz simplemente almacena los elementos en orden de inserción, y toda la combinación en realidad usa menos memoria que la implementación utilizada en Python 3.5 y anteriores. Vea la idea original publicada por Raymond Hettinger para más detalles.

En 3.6 esto todavía se consideraba un detalle de implementación; ver el Novedades de Python 3.6 documentación :

El aspecto de preservación del orden de esta nueva implementación se considera un detalle de implementación y no se debe confiar en él (esto puede cambiar en el futuro, pero se desea tener esta nueva implementación dict en el idioma durante algunas versiones antes de cambiar la especificación del idioma para ordenar la semántica de preservación del orden para todas las implementaciones actuales y futuras de Python; esto también ayuda a preservar la compatibilidad con versiones anteriores del lenguaje donde el orden de iteración aleatoria aún está vigente, por ejemplo, Python 3.5).

Python 3.7 eleva este detalle de implementación a una especificación de lenguaje , por lo que ahora es obligatorio que dictconserve el orden en todas las implementaciones de Python compatibles con esa versión o más nuevas. Ver el pronunciamiento de la BDFL .

Es posible que aún desee utilizar la collections.OrderedDict()clase en ciertos casos, ya que ofrece algunas funcionalidades adicionales además del dicttipo estándar . Como ser reversible (esto se extiende a los objetos de vista ) y admitir la reordenación (a través del move_to_end()método ).

Martijn Pieters
fuente
Lamentablemente, esto no es relevante para la creación del diccionario . No se garantiza que los diccionarios especificados como el del ejemplo en el código mantengan el mismo orden. Esto significa que debe crear un dict vacío e insertar manualmente cada elemento si desea controlar el orden.
naught101
8
@ naught101: no, esto se aplica a la creación de dict. En Python 3.7 y versiones posteriores, el uso de una pantalla dict como se muestra en la pregunta garantiza una lista de esas teclas en orden . Los pares clave-valor tal como están escritos se insertan de izquierda a derecha, porque la especificación del lenguaje garantiza que : Si se proporciona una secuencia separada por comas de pares clave / dato, se evalúan de izquierda a derecha para definir las entradas del diccionario . La dict()documentación incluso incluye un ejemplo.
Martijn Pieters
175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

contiene

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Si los valores son True(o cualquier otro objeto inmutable), también puede usar:

OrderedDict.fromkeys(words, True)
eumiro
fuente
2
Vale la pena señalar, por supuesto, que la parte 'inmutable' no es una regla dura y rápida que Python aplicará, es "solo" una buena idea.
lvc
11
tenga en cuenta que soluciones como: OrderedDict(FUTURE=[], TODAY=[], PAST=[])no funcionarán, cuando se menciona un enfoque: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])mantendrán el orden.
andilabs
2
@andi Tengo otro problema, cuando uso jsonify, el OrderedDict parece perdido su orden cuando se generan los datos json. ¿De alguna manera para resolver esto?
tyan
github.com/pallets/flask/issues/974 esto se puede utilizar para resolver el problema ..
tyan
55
Python3.7 ahora tiene dict ordenado por defecto. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat
167

En lugar de explicar la parte teórica, daré un ejemplo simple.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}
Mohit Dabas
fuente
16
¿Hay alguna forma de asignar en masa OrderedDict como el tipo Dict?
tyan
2
OrderedDictde hecho resuelve el problema, pero ... en este ejemplo en particular obtienes exactamente el mismo resultado usando un diccionario estándar
Tonechas
2
@Tonechas: acabo de probar el ejemplo con un diccionario estándar y obtuve, {'aol': 1, 'foo': 3}así que creo que es un buen ejemplo ilustrativo.
twasbrillig
44
Hay una lección para todos: se descubrió (creo que en torno a la versión 2.4) que el hashing predecible de Python podría generar vulnerabilidades de seguridad , por lo que ahora no hay garantía de que incluso dos ejecuciones diferentes del mismo código den el mismo orden en un estándar dict.
holdenweb
1
@tyan puede llamar OrderedDict.update()con un iterables pares de valores clave que contienen: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen
37

Tenga en cuenta que esta respuesta se aplica a las versiones de python anteriores a python3.7. CPython 3.6 mantiene el orden de inserción en la mayoría de las circunstancias como un detalle de implementación. A partir de Python3.7 en adelante, se ha declarado que las implementaciones DEBEN mantener el orden de inserción para cumplir.


los diccionarios de python no están ordenados. Si desea un diccionario ordenado, intente colecciones.OrderedDict .

Tenga en cuenta que OrderedDict se introdujo en la biblioteca estándar en python 2.7. Si tiene una versión anterior de python, puede encontrar recetas para diccionarios ordenados en ActiveState .

mgilson
fuente
ver la publicación de @ martijn arriba. Desde Python 3.6 en adelante, dict admite el orden de inserción.
tpk
13

Los diccionarios usarán un orden que hace que la búsqueda sea eficiente, y no puede cambiar eso,

Simplemente puede usar una lista de objetos (una tupla de 2 elementos en un caso simple, o incluso una clase), y agregar elementos al final. Luego puede usar la búsqueda lineal para buscar elementos en ella.

Alternativamente, podría crear o utilizar una estructura de datos diferente creada con la intención de mantener el orden.

Lancer de fuego
fuente
Los diccionarios usarán un orden que hace que la búsqueda sea eficiente Finalmente, alguien lo señaló.
scharette
7

Encontré esta publicación mientras intentaba descubrir cómo hacer que OrderedDict funcione. PyDev para Eclipse no pudo encontrar OrderedDict en absoluto, así que decidí hacer una tupla de los valores clave de mi diccionario, ya que me gustaría que se ordenaran. Cuando necesitaba generar mi lista, simplemente iteraba a través de los valores de la tupla y conectaba la 'clave' iterada de la tupla al diccionario para recuperar mis valores en el orden en que los necesitaba.

ejemplo:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

Es un poco engorroso, pero estoy presionado por el tiempo y es la solución que se me ocurrió.

nota: el enfoque de la lista de listas que alguien más sugirió realmente no tiene sentido para mí, porque las listas están ordenadas e indexadas (y también tienen una estructura diferente a la de los diccionarios).

cara sonriente
fuente
Gran solución Lo usaré para escribir json en el archivo, siempre en el mismo orden.
Hrvoje T
6

Realmente no puedes hacer lo que quieres con un diccionario. Ya tienes el diccionario d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}creado. Descubrí que no había forma de mantener el orden una vez que ya estaba creado. Lo que hice fue crear un archivo json con el objeto:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Solía:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

luego utilizado:

print json.dumps(r)

para verificar.

nealous3
fuente
1
Entonces, ¿por qué no comenzar con un OrderedDict de una lista? El archivo JSON realmente no agrega nada aquí.
Martijn Pieters
Sí, la lista es más útil para mantener el orden, pero la respuesta fue con respecto a la pregunta sobre cómo ordenar diccionarios. Simplemente informar a las personas sobre las limitaciones de usar un diccionario y darles una posible solución si necesitan usar un diccionario por alguna razón.
nealous3
1
Pero esa parte ya está cubierta por respuestas mucho más antiguas, que se remontan a 2012.
Martijn Pieters
3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])
usuario11502624
fuente
problema principal que ya no es un dict, es una lista de tuplas
Oleg
2

Otra alternativa es usar Pandas, dataframeya que garantiza el orden y las ubicaciones de índice de los elementos en una estructura tipo dict.

Ville
fuente
1

En general, se puede diseñar una clase que se comporta como un diccionario, principalmente la aplicación de los métodos __contains__, __getitem__, __delitem__, __setitem__y algunos más. Esa clase puede tener cualquier comportamiento que desee, por ejemplo, dividir un iterador ordenado sobre las teclas ...

Rasmus Kaj
fuente
1

si desea tener un diccionario en un orden específico, también puede crear una lista de listas, donde el primer elemento será la clave, y el segundo elemento será el valor y se verá como este ejemplo

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3
pelos
fuente
77
Ese no es un "diccionario" porque no puede buscar elementos por su clave sin buscar a través de toda la colección (tomando tiempo O (n)).
BHSPitMonkey
1
Sí, no es un diccionario, pero, dependiendo de la situación, podría proporcionar una solución válida al problema del póster original.
SunSparc
no dijo exactamente cómo queríamos, solo que quería poder ordenarlos =), como siempre hay muchas maneras de hacer una cosa.
pelos
1

Tuve un problema similar al desarrollar un proyecto Django. No podía usar OrderedDict, porque estaba ejecutando una versión antigua de python, por lo que la solución era usar la clase SortedDict de Django:

https://code.djangoproject.com/wiki/SortedDict

p.ej,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Nota: Esta respuesta es originalmente de 2011. Si tiene acceso a Python versión 2.7 o superior, entonces debería tener acceso al estándar ahora collections.OrderedDict, del cual muchos ejemplos han sido proporcionados por otros en este hilo.

Evan B.
fuente
0

Puedes hacer lo mismo que hice para el diccionario.

Crea una lista y un diccionario vacío:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', '[email protected]']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Himanshu Kanojiya
fuente