¿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 OrderedDictsi 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.6implementació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_entriescontiene 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_indicescontiene los índices para la dk_entriesmatriz (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_indicesy 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( 1byte) hasta int32_t/ int64_t( 4/ 8bytes) en las compilaciones 32/ 64bit)
En la implementación anterior, se tenía que asignar una matriz dispersa de tipo PyDictKeyEntryy 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_sizellena por razones de rendimiento . (¡y el espacio vacío todavía tenía PyDictKeyEntrytamañ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( Xdependiendo del tamaño del dict) 2/3 * dk_sizellena. El espacio vacío cambió de tipo PyDictKeyEntrya intX_t.
Entonces, obviamente, crear una matriz dispersa de tipos PyDictKeyEntryrequiere mucha más memoria que una matriz dispersa para almacenar ints.
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 dictobjeto no proporciona . Los OrderedDicts son reversibles, proporcionan métodos sensibles al orden y, principalmente, proporcionan una prueba de igualdad sensible al orden ( ==, !=). dictActualmente 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.