Set e dadi sono ottimizzati per diversi casi d'uso. L'uso principale di un set è il test rapido dell'appartenenza, che è indipendente dall'ordine. Per i dicts, il costo della ricerca è l'operazione più critica e la chiave ha maggiori probabilità di essere presente. Con i set, la presenza o l'assenza di un elemento non è nota in anticipo, quindi l'implementazione del set deve essere ottimizzata sia per il caso trovato che per quello non trovato. Inoltre, alcune ottimizzazioni per operazioni di set comuni come unione e intersezione rendono difficile mantenere l'ordine dei set senza compromettere le prestazioni.
Mentre entrambe le strutture di dati sono basate sull'hash, è un'idea sbagliata comune che gli insiemi vengano implementati come dicts con valori null. Anche prima dell'implementazione compatta di dict in CPython 3.6, le implementazioni di set e dict differivano già in modo significativo, con un piccolo riutilizzo del codice. Ad esempio, i dicts usano il sondaggio randomizzato, ma i set usano una combinazione di sondaggio lineare e indirizzamento aperto, per migliorare la localizzazione della cache. La sonda lineare iniziale (impostazione predefinita di 9 passaggi in CPython) controllerà una serie di coppie chiave / hash adiacenti, migliorando le prestazioni riducendo il costo della gestione delle collisioni hash - l'accesso consecutivo alla memoria è più economico delle sonde sparse.
In teoria sarebbe possibile cambiare l'implementazione impostata di CPython in modo che sia simile al dict compatto, ma in pratica ci sono degli svantaggi e notevoli sviluppatori core si sono opposti a fare un tale cambiamento.
I set rimangono non ordinati. (Perché? I modelli di utilizzo sono diversi. Inoltre, un'implementazione diversa.)
- Guido van Rossum
I set utilizzano un algoritmo diverso che non è così modificabile per mantenere l'ordine di inserimento. Le operazioni set-to-set perdono flessibilità e ottimizzazioni se è necessario un ordine. La matematica degli insiemi è definita in termini di insiemi non ordinati. In breve, impostare l'ordinamento non è nell'immediato futuro.
- Raymond Hettinger
Una discussione dettagliata sull'opportunità o meno di compattare i set per 3.7 e le risposte sul perché è stato deciso contro, può essere trovata nelle mailing list di python-dev.
In sintesi, i punti principali sono che i modelli di utilizzo sono diversi (dicts di ordinamento di inserimento come ** kwargs è utile , meno per i set), i risparmi di spazio per i set di compattazione sono meno significativi (perché ci sono solo chiavi e array di hash da densify, al contrario di chiavi, hash e valori) e la suddetta ottimizzazione del probing lineare in set è incompatibile con un'implementazione compatta.
Riporterò di seguito il post di Raymond che tratta i punti più importanti.
Il 14 settembre 2016, alle 15:50, Eric Snow ha scritto:
Quindi, farò lo stesso con i set.
A meno che non abbia frainteso, Raymond si è opposto a fare un cambiamento simile per impostare.
Giusto. Ecco alcuni pensieri sull'argomento prima che le persone inizino a correre selvaggiamente.
Per il dict compatto, il risparmio di spazio è stato una vittoria netta con lo spazio aggiuntivo consumato dagli indici e la sovrallocazione per gli array chiave / valore / hash essendo più che compensata dalla migliore densità degli array chiave / valore / hash. Tuttavia, per gli insiemi, la rete era molto meno favorevole perché abbiamo ancora bisogno degli indici e della sovrallocazione ma possiamo compensare il costo dello spazio solo densificando solo due delle tre matrici. In altre parole, la compattazione ha più senso quando si perde spazio per chiavi, valori e hash. Se perdi una di quelle tre, smette di essere avvincente.
Il modello di utilizzo per i set è diverso dai dicts. Il primo ha più ricerche hit o miss. Quest'ultimo tende ad avere meno ricerche chiave mancanti. Inoltre, alcune delle ottimizzazioni per le operazioni da set a set rendono difficile mantenere l'ordine dei set senza influire sulle prestazioni.
Ho perseguito un percorso alternativo per migliorare le prestazioni del set. Invece di compattare (che non era gran parte dello spazio vincente e ha sostenuto il costo di un'ulteriore direzione indiretta), ho aggiunto il sondaggio lineare per ridurre il costo delle collisioni e migliorare le prestazioni della cache. Questo miglioramento è incompatibile con l'approccio di compattazione che ho sostenuto per i dizionari.
Per ora, l'effetto collaterale dell'ordine sui dizionari non è garantito, quindi è prematuro iniziare a insistere che anche i set vengano ordinati. I documenti si collegano già a una ricetta per la creazione di un OrderedSet (
https://code.activestate.com/recipes/576694/ ) ma sembra che l'assorbimento sia stato quasi zero. Inoltre, ora che Eric Snow ci ha dato un veloce OrderedDict, è più facile che mai costruire un OrderedSet da MutableSet e OrderedDict, ma ancora una volta non ho riscontrato alcun interesse reale perché la tipica analisi dei dati set-to-set non realmente necessità o cura dell'ordinazione. Allo stesso modo, l'uso principale dei test rapidi di appartenenza è indipendente dall'ordine.
Detto questo, penso che ci sia spazio per aggiungere implementazioni di set alternativi a PyPI. In particolare, ci sono alcuni casi speciali interessanti per i dati ordinabili in cui le operazioni set-to-set possono essere velocizzate confrontando intere gamme di chiavi (vedi
https://code.activestate.com/recipes/230113-implementation-of- sets-using-ordinati-lists
per un punto di partenza). IIRC, PyPI ha già il codice per i filtri bloom simili a quelli impostati e l'hash del cuculo.
Comprendo che è eccitante avere un blocco di codice maggiore accettato nel core di Python, ma ciò non dovrebbe aprirsi alle porte per impegnarsi in riscritture più importanti di altri tipi di dati a meno che non siamo sicuri che sia garantito.
- Raymond Hettinger
Da [Python-Dev] Python 3.6 dict diventa compatto e ottiene una versione privata; e le parole chiave vengono ordinate , settembre 2016.