Czy słowniki są zamawiane w Pythonie 3.6+?
Są one wstawiane [1] . Począwszy od Pythona 3.6, w implementacji CPython w Pythonie słowniki zapamiętują kolejność wstawianych elementów . Jest to uważane za szczegół implementacji w Pythonie 3.6 ; musisz użyć, OrderedDict
jeśli chcesz porządkować wstawianie, które jest gwarantowane w innych implementacjach Pythona (i innych uporządkowanych zachowaniach [1] ).
Od wersji Python 3.7 nie jest to już szczegół implementacji, a zamiast tego staje się funkcją języka. Z wiadomości napisanej przez GvR w python-dev :
Zrób to tak. „Dict utrzymuje kolejność wstawiania” to orzeczenie. Dzięki!
Oznacza to po prostu, że możesz na tym polegać . Inne implementacje Pythona muszą także oferować słownik z wstawionym słownikiem, jeśli chcą być zgodną implementacją Pythona 3.7.
W jaki sposób 3.6
implementacja słownika Python działa lepiej [2] niż starsza, zachowując kolejność elementów?
Zasadniczo poprzez utrzymanie dwóch tablic .
Pierwsza tablica dk_entries
zawiera wpisy ( typuPyDictKeyEntry
) słownika w kolejności ich wstawienia. Porządek zachowania jest osiągany przez to, że jest to tablica tylko do dołączania, w której nowe elementy są zawsze wstawiane na końcu (kolejność wstawiania).
Drugi, dk_indices
zawiera wskaźniki dla dk_entries
tablicy (czyli wartości wskazujące pozycję odpowiedniego wpisu w dk_entries
). Ta tablica działa jak tablica skrótów. Gdy klucz jest mieszany, prowadzi on do jednego z przechowywanych indeksów, dk_indices
a odpowiedni wpis jest pobierany przez indeksowanie dk_entries
. Ponieważ przechowywane są tylko indeksy, typ tej tablicy zależy od ogólnego rozmiaru słownika (od typu int8_t
( 1
bajt) do int32_t
/ int64_t
( 4
/ 8
bajty) w 32
/ 64
kompilacjach bitowych)
W poprzedniej implementacji konieczne było przydzielenie rzadkiej tablicy typu PyDictKeyEntry
i rozmiaru dk_size
; Niestety, to również spowodowało dużo pustej przestrzeni ponieważ tablica nie wolno było mieć więcej niż 2/3 * dk_size
pełny ze względu na wydajność . (a puste miejsce wciąż miało PyDictKeyEntry
rozmiar!).
Teraz tak nie jest, ponieważ przechowywane są tylko wymagane wpisy (te, które zostały wstawione) i zachowana jest rzadka tablica typu intX_t
(w X
zależności od rozmiaru nagrania) 2/3 * dk_size
pełna. Puste miejsce zmieniło się z typu PyDictKeyEntry
na intX_t
.
Tak więc, oczywiście, tworzenie rzadkiej tablicy typu PyDictKeyEntry
wymaga znacznie więcej pamięci niż rzadka tablica do przechowywania int
s.
Możesz zobaczyć pełną rozmowę na temat Python-Dev dotyczącą tej funkcji, jeśli jesteś zainteresowany, jest to dobra lektura.
W oryginalnej propozycji Raymonda Hettingera można zobaczyć wizualizację zastosowanych struktur danych, która oddaje sedno tego pomysłu.
Na przykład słownik:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
jest obecnie przechowywany jako [skrót, klucz, wartość]:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
Zamiast tego dane należy uporządkować w następujący sposób:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
Jak widać teraz, w oryginalnej propozycji dużo miejsca jest zasadniczo puste, aby zmniejszyć kolizje i przyspieszyć wyszukiwanie. Dzięki nowemu podejściu zmniejszasz wymaganą pamięć, przesuwając rzadkość tam, gdzie jest naprawdę wymagana, w indeksach.
[1]: Mówię „wstawione uporządkowane”, a nie „uporządkowane”, ponieważ przy istnieniu OragedDict „uporządkowane” sugeruje dalsze zachowanie, którego dict
obiekt nie zapewnia . OrdersDicts są odwracalne, zapewniają metody uwzględniające porządek, a przede wszystkim zapewniają testy równości uwzględniające porządek ( ==
, !=
). dict
Obecnie nie oferują żadnego z tych zachowań / metod.
[2]: Nowe implementacje słownika mają lepszą pamięć, ponieważ są bardziej zwarte; to główna zaleta tutaj. Jeśli chodzi o szybkość, różnica nie jest tak drastyczna, są miejsca, w których nowy dyktat może wprowadzić niewielkie regresje ( na przykład wyszukiwanie kluczowych kluczy ), podczas gdy w innych (przychodzą na myśl iteracja i zmiana rozmiaru) powinno być obecne zwiększenie wydajności.
Ogólnie, wydajność słownika, szczególnie w rzeczywistych sytuacjach, poprawia się dzięki wprowadzonej zwartości.