Jak zachować klucze / wartości w tej samej kolejności, w jakiej zostały zadeklarowane?

320

Mam słownik, który zadeklarowałem w określonej kolejności i chcę go cały czas utrzymywać w tej kolejności. Klucze / wartości nie mogą być tak naprawdę uporządkowane na podstawie ich wartości, chcę tylko w kolejności, w której je zadeklarowałem.

Więc jeśli mam słownik:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Nie ma takiej kolejności, jeśli ją przeglądam lub iteruję, czy jest jakiś sposób, aby upewnić się, że Python zachowa jawną kolejność, w której zadeklarowałem klucze / wartości?

roflwaffle
źródło

Odpowiedzi:

229

Począwszy od Python 3.6, dicttyp standardowy domyślnie zachowuje porządek wstawiania.

Definiowanie

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

spowoduje utworzenie słownika z kluczami w kolejności podanej w kodzie źródłowym.

Osiągnięto to poprzez użycie prostej tablicy z liczbami całkowitymi dla rzadkiej tablicy mieszającej, gdzie te liczby całkowite indeksują do innej tablicy, która przechowuje pary klucz-wartość (plus obliczony skrót). Ta ostatnia tablica po prostu przechowuje elementy w kolejności wstawiania, a cała kombinacja faktycznie zużywa mniej pamięci niż implementacja używana w Pythonie 3.5 i wcześniejszych. Szczegółowe informacje można znaleźć w oryginalnym wpisie pomysłu autorstwa Raymonda Hettingera .

W wersji 3.6 nadal uważano to za szczegół implementacji; zobacz dokumentację Co nowego w dokumentacji Python 3.6 :

Aspekt zachowywania porządku w tej nowej implementacji jest uważany za szczegół implementacji i nie należy na nim polegać (może się to zmienić w przyszłości, ale pożądane jest, aby ta nowa implementacja dyktowania w języku była dostępna przez kilka wydań przed zmianą specyfikacji języka narzucenie semantyki zachowującej porządek dla wszystkich bieżących i przyszłych implementacji Pythona; pomaga to również zachować zgodność wsteczną ze starszymi wersjami języka, w którym wciąż obowiązuje kolejność losowych iteracji, np. Python 3.5).

Python 3.7 podnosi ten szczegół implementacji do specyfikacji języka , więc jest teraz obowiązkowy, aby dictzachować porządek we wszystkich implementacjach Python kompatybilnych z tą wersją lub nowszą. Zobacz oświadczenie BDFL .

W niektórych przypadkach nadal możesz chcieć użyć tej collections.OrderedDict()klasy , ponieważ oferuje ona dodatkowe funkcje oprócz standardowego dicttypu. Takich jak istnienie odwracalność (dotyczy to także obiektów widoku ) i obsługa zmiany kolejności ( move_to_end()metodą ).

Martijn Pieters
źródło
Niestety nie ma to znaczenia przy tworzeniu słownika . Słowniki określone tak jak w przykładzie w kodzie nie mają gwarancji utrzymania tej samej kolejności. Oznacza to, że musisz utworzyć pusty dykt i ręcznie wstawić każdy element, jeśli chcesz kontrolować kolejność.
naught101
8
@ naught101: nie, to nie stosuje się do tworzenia dict. W Pythonie 3.7 i nowszych, użycie wyświetlacza dykta, jak pokazano w pytaniu, gwarantuje wyświetlanie tych kluczy w kolejności . Zapisane pary klucz-wartość są wstawiane od lewej do prawej, ponieważ specyfikacja języka gwarantuje, że : Jeśli podana jest rozdzielona przecinkami sekwencja par klucz / dane, są one oceniane od lewej do prawej w celu zdefiniowania pozycji słownika . dict()Dokumentacja zawiera jeszcze jeden przykład.
Martijn Pieters
175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

zawiera

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Jeśli wartości to True(lub jakikolwiek inny niezmienny obiekt), możesz również użyć:

OrderedDict.fromkeys(words, True)
eumiro
źródło
2
Warto oczywiście zauważyć, że „niezmienna” część nie jest twardą i szybką zasadą, którą wymusi Python - to „tylko” dobry pomysł.
lvc,
11
pamiętaj, że rozwiązania takie jak: nie OrderedDict(FUTURE=[], TODAY=[], PAST=[])będą działać, gdy wspomniane podejście: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])utrzyma porządek.
andilabs
2
@i mam inny problem: podczas korzystania z jsonify, OrDERDict wydaje się stracony, kiedy generuje dane json.
tyan
github.com/pallets/flask/issues/974 można to wykorzystać do rozwiązania problemu ..
tyan
5
Python3.7 ma teraz domyślnie uporządkowany słownik. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat
167

Zamiast wyjaśniać część teoretyczną podam prosty przykład.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}
Mohit Dabas
źródło
16
Czy istnieje sposób masowego przypisywania polecenia OragedDict, takiego jak typ Dict?
tyan
2
OrderedDictrzeczywiście rozwiązuje problem, ale ... w tym konkretnym przykładzie uzyskuje się dokładnie ten sam wynik przy użyciu standardowego słownika
Tonechas
2
@Tonechas: Właśnie wypróbowałem ten przykład ze standardowym słownikiem i mam, {'aol': 1, 'foo': 3}więc myślę, że to dobry przykład ilustrujący.
twasbrillig
4
Wszyscy mają lekcję: odkryłem (myślę o wydaniu 2.4), że przewidywalne haszowanie Pythona może powodować luki w zabezpieczeniach , więc teraz nie ma gwarancji, że nawet dwa różne uruchomienia tego samego kodu dadzą taką samą kolejność w standardzie dyktować.
holdenweb,
1
@tyan można zadzwonić OrderedDict.update()z iterable zawierających par klucz-wartość: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen,
37

Zauważ, że ta odpowiedź dotyczy wersji Pythona wcześniejszych niż Python3.7. CPython 3.6 utrzymuje porządek wstawiania w większości przypadków jako szczegół implementacji. Począwszy od wersji Python 3.7, zadeklarowano, że implementacje MUSZĄ zachować kolejność wstawiania, aby była zgodna.


słowniki python są nieuporządkowane. Jeśli chcesz uporządkowanego słownika, wypróbuj kolekcje .

Zwróć uwagę, że w standardowej bibliotece Python 2.7 wprowadzono OrdersDict. Jeśli masz starszą wersję Pythona, możesz znaleźć przepisy na uporządkowane słowniki na ActiveState .

mgilson
źródło
patrz post @ martijn powyżej. Począwszy od Pythona 3.6, dict obsługuje kolejność wstawiania.
tpk
13

Słowniki używają kolejności, która sprawia, że ​​wyszukiwanie jest wydajne, a ty nie możesz tego zmienić,

Możesz po prostu użyć listy obiektów (krotka 2-elementowa w prostym przypadku, a nawet klasy) i dołączyć elementy na końcu. Następnie możesz użyć wyszukiwania liniowego, aby znaleźć w nim elementy.

Alternatywnie możesz utworzyć lub użyć innej struktury danych utworzonej z zamiarem utrzymania porządku.

Lansjer ognia
źródło
Słowniki wykorzystują porządek, który sprawia, że ​​wyszukiwanie jest efektywne Wreszcie ktoś zwrócił na to uwagę.
scharette
7

Natknąłem się na ten post, próbując dowiedzieć się, jak nakłonić OrDERDict do pracy. PyDev dla Eclipse w ogóle nie mógł znaleźć OrdersDict, więc ostatecznie zdecydowałem się stworzyć krotkę kluczowych wartości ze słownika, tak jak chciałbym, aby były uporządkowane. Kiedy musiałem wypisać moją listę, właśnie wykonałem iterację wartości krotki i podłączyłem iterowany „klucz” z krotki do słownika, aby pobrać moje wartości w kolejności, w jakiej ich potrzebowałem.

przykład:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

Jest to trochę uciążliwe, ale mam presję czasu i jest to obejście.

Uwaga: podejście do listy list sugerowane przez kogoś innego nie ma dla mnie sensu, ponieważ listy są uporządkowane i indeksowane (a także mają inną strukturę niż słowniki).

smushface
źródło
Świetne rozwiązanie. Użyję go do napisania pliku JSON do pliku, zawsze w tej samej kolejności.
Hrvoje T
6

Za pomocą słownika nie można naprawdę robić tego, co chcesz. Słownik został już d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}utworzony. Odkryłem, że nie ma sposobu, aby utrzymać porządek, gdy jest już utworzony. Zamiast tego utworzyłem plik json z obiektem:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Użyłem:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

następnie użyty:

print json.dumps(r)

do weryfikacji.

nealous3
źródło
1
Dlaczego więc nie zacząć od OrdictDict z listy? Plik JSON tak naprawdę niczego tu nie dodaje.
Martijn Pieters
Tak, lista jest bardziej przydatna do utrzymania porządku, ale odpowiedź dotyczyła pytania o zamawianie słowników. Po prostu informowanie ludzi o ograniczeniach korzystania ze słownika i dawanie im możliwości obejścia, jeśli z jakiegoś powodu będą potrzebować słownika.
nealous3
1
Ale ta część jest już objęta znacznie starszymi odpowiedziami, datowanymi na 2012 rok.
Martijn Pieters
3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])
użytkownik11502624
źródło
głównym problemem, który nie jest już dyktando, jest lista krotek
Oleg
2

Inną alternatywą jest użycie Pand, dataframeponieważ gwarantuje to porządek i lokalizację indeksów przedmiotów w strukturze przypominającej dict.

Ville
źródło
1

Generalnie, można zaprojektować klasy, która zachowuje się jak słownik, będzie głównie realizacji metody __contains__, __getitem__, __delitem__, __setitem__i trochę więcej. Ta klasa może mieć dowolne zachowanie, na przykład uprzywilejowanie posortowanego iteratora nad klawiszami ...

Rasmus Kaj
źródło
1

jeśli chcesz mieć słownik w określonej kolejności, możesz również utworzyć listę list, w której pierwszy element będzie kluczem, a drugi element będzie wartością i będzie wyglądał tak jak w tym przykładzie

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3
pelos
źródło
7
To nie jest „słownik”, ponieważ nie można wyszukiwać elementów według ich klucza bez przeszukiwania całej kolekcji (zajmuje to czas O (n)).
BHSPitMonkey
1
Tak, to nie jest słownik, ale w zależności od sytuacji może stanowić prawidłowe rozwiązanie problemu z oryginalnym plakatem.
SunSparc,
nie powiedział dokładnie, jak chcieliśmy, po prostu chcą móc je zamówić =), ponieważ zawsze istnieje wiele sposobów na zrobienie jednej rzeczy.
pelos
1

Miałem podobny problem podczas opracowywania projektu Django. Nie mogłem użyć OrdersDict, ponieważ miałem starą wersję Pythona, więc rozwiązaniem było użycie klasy SortedDict Django:

https://code.djangoproject.com/wiki/SortedDict

na przykład,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Uwaga: Ta odpowiedź pochodzi z 2011 roku. Jeśli masz dostęp do Pythona w wersji 2.7 lub nowszej, powinieneś mieć dostęp do standardu teraz collections.OrderedDict, którego wiele przykładów zostało podanych przez innych w tym wątku.

Evan B.
źródło
0

Możesz zrobić to samo, co ja dla słownika.

Utwórz listę i opróżnij słownik:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', '[email protected]']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Himanshu Kanojiya
źródło