Los diccionarios se ordenan en Python 3.6 (al menos en la implementación de CPython) a diferencia de las encarnaciones anteriores. Esto parece un cambio sustancial, pero es solo un breve párrafo en la documentación . Se describe como un detalle de implementación de CPython en lugar de una función de lenguaje, pero también implica que esto puede convertirse en estándar en el futuro.
¿Cómo funciona mejor la nueva implementación del diccionario que la anterior al tiempo que conserva el orden de los elementos?
Aquí está el texto de la documentación:
dict()
ahora usa una representación "compacta" iniciada por PyPy . El uso de memoria del nuevo dict () es entre un 20% y un 25% menor en comparación con Python 3.5. PEP 468 (Preservar el orden de ** kwargs en una función) es implementado por esto. 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 de dict en el idioma durante algunos lanzamientos 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 todavía está vigente, por ejemplo, Python 3.5). (Contribuido por INADA Naoki ennúmero 27350 . Idea sugerida originalmente por Raymond Hettinger .)
Actualización de diciembre de 2017: dict
el orden de inserción de retención está garantizado para Python 3.7
fuente
**kwargs
y, como tal, la redacción utilizada es diplomática:**kwargs
en una función, la firma ahora se garantiza que es un mapeo de preservación del orden de inserción . Han utilizado el término mapeo para no forzar ninguna otra implementación para hacer que el dict sea ordenado (y usarloOrderedDict
internamente) y como una forma de indicar que no se supone que esto dependa del hecho de quedict
no está ordenado.Respuestas:
Se ordenan por inserción [1] . A partir de Python 3.6, para la implementación CPython de Python, los diccionarios recuerdan el orden de los elementos insertados . Esto se considera un detalle de implementación en Python 3.6 ; debe usarlo
OrderedDict
si desea un orden de inserción garantizado en otras implementaciones de Python (y otro comportamiento ordenado [1] ).A partir de Python 3.7 , esto ya no es un detalle de implementación, sino que se convierte en una característica del lenguaje. De un mensaje python-dev de GvR :
Esto simplemente significa que puede confiar en ello . Otras implementaciones de Python también deben ofrecer un diccionario de inserción ordenada si desean ser una implementación conforme de Python 3.7.
Básicamente, manteniendo dos matrices .
La primera matriz,
dk_entries
contiene las entradas ( de tipoPyDictKeyEntry
) para el diccionario en el orden en que se insertaron. El orden de preservación se logra al ser una matriz de agregar solo donde siempre se insertan nuevos elementos al final (orden de inserción).El segundo,
dk_indices
contiene los índices para ladk_entries
matriz (es decir, valores que indican la posición de la entrada correspondiente endk_entries
). Esta matriz actúa como la tabla hash. Cuando se codifica una clave, conduce a uno de los índices almacenadosdk_indices
y la entrada correspondiente se obtiene mediante indexacióndk_entries
. Dado que solo se mantienen los índices, el tipo de esta matriz depende del tamaño general del diccionario (que va desde el tipoint8_t
(1
byte) hastaint32_t
/int64_t
(4
/8
bytes) en las compilaciones32
/64
bit)En la implementación anterior, se tenía que asignar una matriz dispersa de tipo
PyDictKeyEntry
y tamañodk_size
; desafortunadamente, también resultó en mucho espacio vacío ya que no se permitió que esa matriz estuviera más que2/3 * dk_size
llena por razones de rendimiento . (¡y el espacio vacío todavía teníaPyDictKeyEntry
tamaño!).Este no es el caso ahora, ya que solo se almacenan las entradas requeridas (las que se han insertado) y se mantiene una matriz dispersa de tipo
intX_t
(X
dependiendo del tamaño del dict)2/3 * dk_size
llena. El espacio vacío cambió de tipoPyDictKeyEntry
aintX_t
.Entonces, obviamente, crear una matriz dispersa de tipos
PyDictKeyEntry
requiere mucha más memoria que una matriz dispersa para almacenarint
s.Puede ver la conversación completa en Python-Dev con respecto a esta característica si está interesado, es una buena lectura.
En la propuesta original hecha por Raymond Hettinger , se puede ver una visualización de las estructuras de datos utilizadas que captura la esencia de la idea.
Como puede ver visualmente ahora, en la propuesta original, una gran cantidad de espacio está esencialmente vacío para reducir las colisiones y agilizar las búsquedas. Con el nuevo enfoque, reduce la memoria requerida al mover la escasez donde realmente se requiere, en los índices.
[1]: Digo "inserción ordenada" y no "ordenada" ya que, con la existencia de OrderedDict, "ordenada" sugiere un comportamiento adicional que el
dict
objeto no proporciona . Los OrderedDicts son reversibles, proporcionan métodos sensibles al orden y, principalmente, proporcionan una prueba de igualdad sensible al orden (==
,!=
).dict
Actualmente no ofrecemos ninguno de esos comportamientos / métodos.[2]: Las nuevas implementaciones de diccionario funcionan mejor en cuanto a memoria al estar diseñadas de manera más compacta; Ese es el principal beneficio aquí. En cuanto a la velocidad, la diferencia no es tan drástica, hay lugares donde el nuevo dict podría introducir leves regresiones ( búsquedas de teclas, por ejemplo ), mientras que en otros (la iteración y el cambio de tamaño vienen a la mente) un aumento de rendimiento debería estar presente.
En general, el rendimiento del diccionario, especialmente en situaciones de la vida real, mejora debido a la compacidad introducida.
fuente
entries
cambiado el tamaño de la lista? o se mantiene un espacio en blanco? o está comprimido de vez en cuando?DKIX_DUMMY
un valor de-2
y la entrada en laentry
matriz se reemplaza porNULL
, cuando se realiza la inserción, los nuevos valores se agregan a la matriz de entradas, aún no he podido discernir, pero bastante seguro cuando se llenan los índices más allá del2/3
umbral, se realiza el cambio de tamaño. Esto puede llevar a una reducción en lugar de crecer siDUMMY
existen muchas entradas.d = {i:i for i in range(100)}
y que.pop
todos los elementos w / o la inserción, el tamaño no va a cambiar. Cuando se agrega nuevamente,d[1] = 1
se calcula el tamaño apropiado y se redimensiona el dict.dict
ser ordenado',dict
no se ordenó en el sentido que síOrderedDict
. La cuestión notable es la igualdad.dict
s tienen orden insensible==
,OrderedDict
s tienen orden sensible. VolcarOrderedDict
y cambiardicts
para tener ahora comparaciones sensibles al orden podría provocar una gran ruptura en el código antiguo. Supongo que lo único que puede cambiar sobreOrderedDict
s es su implementación.A continuación se responde la primera pregunta original:
Creo que esta oración de la documentación es suficiente para responder a su pregunta.
dict
no está destinado explícitamente a ser una colección ordenada, por lo tanto, si desea mantenerse constante y no confiar en un efecto secundario de la nueva implementación, debe seguirOrderedDict
.Haga su código a prueba de futuro :)
Hay un debate sobre eso aquí .
EDITAR: Python 3.7 mantendrá esto como una función ver
fuente
Actualización: Guido van Rossum anunció en la lista de correo que a partir de Python 3.7
dict
s en todas las implementaciones de Python debe preservar el orden de inserción.fuente
move_to_end
método y su igualdad es sensible al orden: docs.python.org/3/library/… . Ver la nota sobre la respuesta de Jim Fasarakis Hilliard.Quería agregar a la discusión anterior, pero no tengo la reputación de comentar.
Python 3.8 aún no se ha lanzado del todo, pero incluso incluirá la
reversed()
función en los diccionarios (eliminando otra diferencia deOrderedDict
.No veo ninguna mención del operador de igualdad u otras características, por
OrderedDict
lo que todavía no son del todo iguales.fuente