I dizionari sono ordinati in Python 3.6+?
Sono ordinati per inserzione [1] . A partire da Python 3.6, per l'implementazione CPython di Python, i dizionari ricordano l'ordine degli elementi inseriti . Questo è considerato un dettaglio di implementazione in Python 3.6 ; devi usarlo OrderedDict
se desideri un ordine di inserzione garantito su altre implementazioni di Python (e altri comportamenti ordinati [1] ).
A partire da Python 3.7 , questo non è più un dettaglio di implementazione e diventa invece una funzionalità linguistica. Da un messaggio python-dev di GvR :
Rendilo così. "Dict mantiene l'ordine di inserimento" è la sentenza. Grazie!
Questo significa semplicemente che puoi dipendere da esso . Altre implementazioni di Python devono anche offrire un dizionario ordinato per inserzione se desiderano essere un'implementazione conforme di Python 3.7.
In che modo l' 3.6
implementazione del dizionario Python funziona meglio [2] di quella precedente preservando l'ordine degli elementi?
In sostanza, mantenendo due array .
Il primo array dk_entries
contiene le voci ( di tipoPyDictKeyEntry
) per il dizionario nell'ordine in cui sono state inserite. L'ordine di conservazione si ottiene essendo un array solo append in cui i nuovi elementi vengono sempre inseriti alla fine (ordine di inserimento).
Il secondo, dk_indices
contiene gli indici per l' dk_entries
array (ovvero i valori che indicano la posizione della voce corrispondente in dk_entries
). Questo array funge da tabella hash. Quando una chiave viene cancellata, si ottiene uno degli indici memorizzati dk_indices
e la voce corrispondente viene recuperata tramite l'indicizzazione dk_entries
. Poiché vengono mantenuti solo gli indici, il tipo di questo array dipende dalla dimensione complessiva del dizionario (che va dal tipo int8_t
( 1
byte) a int32_t
/ int64_t
( 4
/ 8
byte) su 32
/ 64
build di bit)
Nell'implementazione precedente, era necessario allocare una matrice sparsa di tipo PyDictKeyEntry
e dimensione dk_size
; sfortunatamente, ha anche provocato un sacco di spazio vuoto poiché a quell'array non è stato permesso di essere più che 2/3 * dk_size
pieno per motivi di prestazioni . (e lo spazio vuoto aveva ancoraPyDictKeyEntry
dimensioni!).
Questo non è il caso ora poiché sono memorizzate solo le voci richieste (quelle che sono state inserite) e una matrice sparsa di tipo intX_t
(a X
seconda della dimensione del dict) 2/3 * dk_size
viene mantenuta piena. Lo spazio vuoto è cambiato da tipo PyDictKeyEntry
a intX_t
.
Quindi, ovviamente, la creazione di una matrice sparsa di tipo PyDictKeyEntry
richiede molta più memoria rispetto a una matrice sparsa per la memorizzazione di int
s.
Puoi vedere la conversazione completa su Python-Dev in merito a questa funzione se ti interessa, è una buona lettura.
Nella proposta originale fatta da Raymond Hettinger , si può vedere una visualizzazione delle strutture dati utilizzate che cattura l'essenza dell'idea.
Ad esempio, il dizionario:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
è attualmente memorizzato come [keyhash, key, value]:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
Invece, i dati dovrebbero essere organizzati come segue:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
Come puoi vedere ora visivamente, nella proposta originale, molto spazio è essenzialmente vuoto per ridurre le collisioni e velocizzare le ricerche. Con il nuovo approccio, riduci la memoria richiesta spostando la scarsità dove è realmente richiesta, negli indici.
[1]: dico "inserimento ordinato" e non "ordinato" poiché, con l'esistenza di OrderedDict, "ordinato" suggerisce ulteriori comportamenti che l' dict
oggetto non fornisce . I OrderedDict sono reversibili, forniscono metodi sensibili all'ordine e, principalmente, forniscono test di uguaglianza sensibili all'ordine ( ==
, !=
). dict
s attualmente non offrono nessuno di questi comportamenti / metodi.
[2]: Le nuove implementazioni del dizionario eseguono meglio la memoria in quanto progettate in modo più compatto; questo è il vantaggio principale qui. Per quanto riguarda la velocità, la differenza non è così drastica, ci sono luoghi in cui il nuovo dict potrebbe introdurre lievi regressioni ( ricerche di chiavi, ad esempio ) mentre in altri (mi viene in mente iterazione e ridimensionamento) dovrebbe essere presente un aumento delle prestazioni.
Nel complesso, le prestazioni del dizionario, soprattutto nelle situazioni di vita reale, migliorano grazie alla compattezza introdotta.