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 OrderedDictse 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.6implementazione del dizionario Python funziona meglio [2] di quella precedente preservando l'ordine degli elementi?
In sostanza, mantenendo due array .
Il primo array dk_entriescontiene 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_indicescontiene gli indici per l' dk_entriesarray (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_indicese 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( 1byte) a int32_t/ int64_t( 4/ 8byte) su 32/ 64build di bit)
Nell'implementazione precedente, era necessario allocare una matrice sparsa di tipo PyDictKeyEntrye 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_sizepieno 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 Xseconda della dimensione del dict) 2/3 * dk_sizeviene mantenuta piena. Lo spazio vuoto è cambiato da tipo PyDictKeyEntrya intX_t.
Quindi, ovviamente, la creazione di una matrice sparsa di tipo PyDictKeyEntryrichiede molta più memoria rispetto a una matrice sparsa per la memorizzazione di ints.
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' dictoggetto non fornisce . I OrderedDict sono reversibili, forniscono metodi sensibili all'ordine e, principalmente, forniscono test di uguaglianza sensibili all'ordine ( ==, !=). dicts 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.