Questa è più una risposta a Python 3.41 Un set prima che fosse chiuso come duplicato.
Gli altri hanno ragione: non fare affidamento sull'ordine. Non fingere nemmeno che ce ne sia uno.
Detto questo, c'è una cosa su cui puoi contare:
list(myset) == list(myset)
Cioè, l'ordine è stabile .
Capire perché esiste un ordine percepito richiede la comprensione di alcune cose:
Che Python utilizza set di hash ,
Come il set di hash di CPython è archiviato in memoria e
Come i numeri vengono cancellati
Dall'alto:
Un set di hash è un metodo per archiviare dati casuali con tempi di ricerca molto rapidi.
Ha un array di supporto:
# A C array; items may be NULL,
# a pointer to an object, or a
# special dummy object
_ _ 4 _ _ 2 _ _ 6
Ignoreremo l'oggetto fittizio speciale, che esiste solo per semplificare la gestione delle rimozioni, poiché non rimuoveremo da questi set.
Per avere una ricerca davvero veloce, fai un po 'di magia per calcolare un hash da un oggetto. L'unica regola è che due oggetti uguali abbiano lo stesso hash. (Ma se due oggetti hanno lo stesso hash possono essere disuguali.)
Quindi fai l'indice prendendo il modulo per la lunghezza dell'array:
hash(4) % len(storage) = index 2
Questo rende veramente veloce l'accesso agli elementi.
Gli hash sono solo la maggior parte della storia, come hash(n) % len(storage)
e hash(m) % len(storage)
possono provocare lo stesso numero. In tal caso, diverse strategie diverse possono tentare di risolvere il conflitto. CPython usa il "probing lineare" 9 volte prima di fare cose complicate, quindi guarderà a sinistra dello slot per un massimo di 9 posti prima di cercare altrove.
I set di hash di CPython sono memorizzati in questo modo:
Un set di hash non può essere più di 2/3 completo . Se sono presenti 20 elementi e l'array di backup è lungo 30 elementi, l'archivio di backup verrà ridimensionato per essere più grande. Questo perché si ottengono collisioni più spesso con piccoli negozi di supporto e le collisioni rallentano tutto.
Il negozio di supporto si ridimensiona in potenze di 4, a partire da 8, ad eccezione di grandi set (50k elementi) che si ridimensionano in potenze di due: (8, 32, 128, ...).
Pertanto, quando si crea un array, l'archivio di supporto ha lunghezza 8. Quando è 5 pieno e si aggiunge un elemento, conterrà brevemente 6 elementi. 6 > ²⁄₃·8
quindi questo attiva un ridimensionamento e il negozio di supporto quadruplica alla dimensione 32.
Infine, hash(n)
restituisce solo i n
numeri (tranne -1
che è speciale).
Quindi, diamo un'occhiata al primo:
v_set = {88,11,1,33,21,3,7,55,37,8}
len(v_set)
è 10, quindi il negozio di supporto è almeno 15 (+1) dopo che tutti gli articoli sono stati aggiunti . Il potere rilevante di 2 è 32. Quindi il negozio di supporto è:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
abbiamo
hash(88) % 32 = 24
hash(11) % 32 = 11
hash(1) % 32 = 1
hash(33) % 32 = 1
hash(21) % 32 = 21
hash(3) % 32 = 3
hash(7) % 32 = 7
hash(55) % 32 = 23
hash(37) % 32 = 5
hash(8) % 32 = 8
quindi questi inseriscono come:
__ 1 __ 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
33 ← Can't also be where 1 is;
either 1 or 33 has to move
Quindi ci aspetteremmo un ordine simile
{[1 or 33], 3, 37, 7, 8, 11, 21, 55, 88}
con 1 o 33 che non è all'inizio altrove. Questo utilizzerà il probing lineare, quindi avremo:
↓
__ 1 33 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
o
↓
__ 33 1 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
Potresti aspettarti che il 33 sia quello che è stato spostato perché l'1 era già lì, ma a causa del ridimensionamento che accade mentre il set viene creato, questo non è in realtà il caso. Ogni volta che il set viene ricostruito, gli elementi già aggiunti vengono effettivamente riordinati.
Ora puoi vedere perché
{7,5,11,1,4,13,55,12,2,3,6,20,9,10}
potrebbe essere in ordine. Ci sono 14 elementi, quindi il negozio di supporto è almeno 21 + 1, il che significa 32:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
1 a 13 hash nei primi 13 slot. 20 va nello slot 20.
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ __ __ __ __ __ __ __ __ __
55 va nello slot hash(55) % 32
che è 23:
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ 55 __ __ __ __ __ __ __ __
Se invece scegliessimo 50, ci aspetteremmo
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ 50 __ 20 __ __ __ __ __ __ __ __ __ __ __
Ed ecco:
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 50}
#>>> {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 50, 20}
pop
è implementato semplicemente dall'aspetto delle cose: attraversa l'elenco e fa apparire il primo.
Questo è tutto dettaglio di implementazione.