¿Se ordenan los diccionarios en Python 3.6+?
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 :
Hazlo así. "Dict mantiene orden de inserción" es el fallo. ¡Gracias!
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.
¿Cómo funciona mejor la 3.6
implementación del diccionario Python [2] que la anterior al tiempo que conserva el orden de los elementos?
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 la dk_entries
matriz (es decir, valores que indican la posición de la entrada correspondiente en dk_entries
). Esta matriz actúa como la tabla hash. Cuando se codifica una clave, conduce a uno de los índices almacenados dk_indices
y la entrada correspondiente se obtiene mediante indexación dk_entries
. Dado que solo se mantienen los índices, el tipo de esta matriz depende del tamaño general del diccionario (que va desde el tipo int8_t
( 1
byte) hasta int32_t
/ int64_t
( 4
/ 8
bytes) en las compilaciones 32
/ 64
bit)
En la implementación anterior, se tenía que asignar una matriz dispersa de tipo PyDictKeyEntry
y tamaño dk_size
; desafortunadamente, también resultó en mucho espacio vacío ya que no se permitió que esa matriz estuviera más que 2/3 * dk_size
llena por razones de rendimiento . (¡y el espacio vacío todavía tenía PyDictKeyEntry
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 tipo PyDictKeyEntry
a intX_t
.
Entonces, obviamente, crear una matriz dispersa de tipos PyDictKeyEntry
requiere mucha más memoria que una matriz dispersa para almacenar int
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.
Por ejemplo, el diccionario:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
actualmente está almacenado como [keyhash, clave, valor]:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
En cambio, los datos deben organizarse de la siguiente manera:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
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.