W Pythonie, jak iterować słownik w posortowanej kolejności kluczy?


211

Istnieje istniejąca funkcja, która kończy się następująco, gdzie djest słownik:

return d.iteritems()

, która zwraca niesortowany iterator dla danego słownika. Chciałbym zwrócić iterator, który przechodzi przez pozycje posortowane według klucza . W jaki sposób mogę to zrobić?

Odpowiedzi:


171

Nie testowałem tego bardzo dokładnie, ale działa w Pythonie 2.5.2.

>>> d = {"x":2, "h":15, "a":2222}
>>> it = iter(sorted(d.iteritems()))
>>> it.next()
('a', 2222)
>>> it.next()
('h', 15)
>>> it.next()
('x', 2)
>>>

Jeśli jesteś przyzwyczajony do robienia for key, value in d.iteritems(): ...zamiast iteratorów, nadal będzie to działać z powyższym rozwiązaniem

>>> d = {"x":2, "h":15, "a":2222}
>>> for key, value in sorted(d.iteritems()):
>>>     print(key, value)
('a', 2222)
('h', 15)
('x', 2)
>>>

W Pythonie 3.x użyj d.items()zamiast, d.iteritems()aby zwrócić iterator.


29
użyj .items()zamiast iteritems(): jak powiedział @Claudiu, iterity nie działają dla Python 3.x, ale items()są dostępne z Python 2.6.
Remi

40
To nie jest oczywiste. W rzeczywistości items()tworzy listę i dlatego używa pamięci, podczas gdy iteritems()zasadniczo nie używa pamięci. Czego używać zależy głównie od wielkości słownika. Ponadto automatyczne narzędzie do konwersji Python 2 na Python 3 ( 2to3) automatycznie zajmuje się konwersją z iteritems()na items(), więc nie musisz się tym martwić.
Eric O Lebigot,

5
@ HowerHell użyj a collections.OrderedDictnastępnie sortujesz raz i zawsze otrzymujesz przedmioty w posortowanej kolejności.
Mark Harviston

9
Ale @EOL, nawet jeśli iteritems()nie używa pamięci, wszystko musi zostać wciągnięte do pamięci sorted(), więc nie ma różnicy między użytkowaniem items()i iteritems()tutaj pod względem pamięci.
Richard

8
@ Richard: Chociaż prawdą jest, że wszystkie elementy muszą zostać wciągnięte do pamięci, są one przechowywane dwa razy za pomocą items()(na liście zwróconej przez items()i na posortowanej liście) i tylko raz za pomocą iteritems()(tylko na posortowanej liście).
Eric O Lebigot,

83

Użyj sorted()funkcji:

return sorted(dict.iteritems())

Jeśli chcesz rzeczywistego iteratora nad posortowanymi wynikami, ponieważ sorted()zwraca listę, użyj:

return iter(sorted(dict.iteritems()))

Dla mnie to się nie udaje: <type 'wyjątki.TypeError'>: iter () zwrócił nie-iterator typu 'list'
mike

Prawdopodobnie dlatego, że używasz „dict” jako nazwy zmiennej. „dict” to w rzeczywistości nazwa typu słowników. Wystarczy użyć innej nazwy, takiej jak „mydict” tutaj i voila.
utku_karatas

1
Wciąż nie działa. Czy jesteś pozytywnie posortowany () zwraca inny iterator, w przeciwieństwie do zwykłej listy?
Mike

kiedy i gdzie występuje ten wyjątek? bez problemu możesz iterować listę

1
Zgoda, chmielu. Nie sądzę, żebym kiedykolwiek wywoływał bezpośrednio .next (), chyba że pomijam wiersze w plikach. Nasze rozwiązanie iteracyjne (posortowane (dict.iteritems ())) kończy się tworzeniem kopii całego dykta w pamięci na etapie „posortowane (”), więc główna zaleta iteratora wydaje się utracona :)

39

Klucze dykta są przechowywane w tablicy hasht, więc jest to ich „naturalny porządek”, tzn. Psuedo-random. Każde inne zamówienie jest koncepcją konsumenta dykta.

sorted () zawsze zwraca listę, a nie dykt. Jeśli przekażesz mu dict.items () (który tworzy listę krotek), zwróci listę krotek [(k1, v1), (k2, v2), ...], których można użyć w pętli w sposób bardzo podobny do dykt, ale w żadnym wypadku nie jest to dykt !

foo = {
    'a':    1,
    'b':    2,
    'c':    3,
    }

print foo
>>> {'a': 1, 'c': 3, 'b': 2}

print foo.items()
>>> [('a', 1), ('c', 3), ('b', 2)]

print sorted(foo.items())
>>> [('a', 1), ('b', 2), ('c', 3)]

Poniższe wygląda jak dykta w pętli, ale tak nie jest, to lista krotek rozpakowywanych do k, v:

for k,v in sorted(foo.items()):
    print k, v

Z grubsza odpowiada:

for k in sorted(foo.keys()):
    print k, foo[k]

Okej, ale nie chcę dyktanda ani listy, chcę iteratora. Jak zmusić go do bycia iteratorem?
Mike

2
sorted(foo.keys())jest lepszy jako odpowiednik sorted(foo), ponieważ słowniki zwracają klucze po zakończeniu iteracji (z tą zaletą, że może nie być zmuszone do utworzenia foo.keys()listy pośredniej, w zależności od sposobu sorted()implementacji iteracji).
Eric O Lebigot,

Zastanawiam się, co jest lepsze ze względu na szybkość i / lub pamięć, k in sorted(foo.keys()):która wyciąga klucze lub for k,v in sorted(foo.items()):która zwraca kopię par listy słownika, zgadujęsorted(foo.keys())
CrandellWS

1
@CrandellWS: Najlepszym sposobem na udzielenie odpowiedzi na pytanie czasowe jest moduł timeit Pythona .
Peter Rowell,

1
@frank - Krótka odpowiedź: Nie. dict jest tablicą z rzeczywistą klucz będący hash wartości klucza dostarczonego. Chociaż niektóre implementacje mogą być dość przewidywalne, a niektóre mogą nawet zawrzeć tę umowę, nie liczę na nic, jeśli chodzi o zamawianie skrótów. Zobacz ten post, aby uzyskać więcej informacji o zachowaniu w wersji 3.6+. W szczególności zwróć uwagę na pierwszą odpowiedź.
Peter Rowell

31

Odpowiedź Grega jest słuszna. Zauważ, że w Python 3.0 musisz to zrobić

sorted(dict.items())

jak iteritemszniknie.


To mi się nie udaje: <type 'wyjątki.TypeError'>: iter () zwrócił nie-iterator typu 'list'
mike

3
„Nie korzystaj z samochodów, ponieważ w przyszłości będziemy mieć hoverboardy”
JJ

7

Możesz teraz używać również OrderedDictw Pythonie 2.7:

>>> from collections import OrderedDict
>>> d = OrderedDict([('first', 1),
...                  ('second', 2),
...                  ('third', 3)])
>>> d.items()
[('first', 1), ('second', 2), ('third', 3)]

Tutaj masz nową stronę dla wersji 2.7 i interfejs API OrdersDict .


To zwróci klucz, wartości w kolejności, w której zostały wstawione - a nie w posortowanej kolejności (tj. Alfabetycznej).
Tony Suffolk 66,

5

Zasadniczo można sortować taki dyktat:

for k in sorted(d):
    print k, d[k]

W konkretnym przypadku w pytaniu, mając „spadek zastępowania” dla d.iteritems (), dodaj funkcję:

def sortdict(d, **opts):
    # **opts so any currently supported sorted() options can be passed
    for k in sorted(d, **opts):
        yield k, d[k]

i tak zmienia się linia końcowa

return dict.iteritems()

do

return sortdict(dict)

lub

return sortdict(dict, reverse = True)

5
>>> import heapq
>>> d = {"c": 2, "b": 9, "a": 4, "d": 8}
>>> def iter_sorted(d):
        keys = list(d)
        heapq.heapify(keys) # Transforms to heap in O(N) time
        while keys:
            k = heapq.heappop(keys) # takes O(log n) time
            yield (k, d[k])


>>> i = iter_sorted(d)
>>> for x in i:
        print x


('a', 4)
('b', 9)
('c', 2)
('d', 8)

Ta metoda wciąż ma sortowanie O (N log N), jednak po krótkim liniowym rozsypywaniu zwraca elementy w posortowanej kolejności, dzięki czemu teoretycznie jest bardziej wydajna, gdy nie zawsze potrzebujesz całej listy.


4

Jeśli chcesz sortować według kolejności wstawiania elementów zamiast kolejności kluczy, zajrzyj do kolekcji Pythona . (Tylko Python 3)


3

sorted zwraca listę, stąd błąd, gdy próbujesz iterować nad nią, ale ponieważ nie możesz zamówić nagrania, będziesz musiał poradzić sobie z listą.

Nie mam pojęcia, jaki jest większy kontekst twojego kodu, ale możesz spróbować dodać iterator do wynikowej listy. może tak ?:

return iter(sorted(dict.iteritems()))

oczywiście będziesz teraz wracał krotki, ponieważ posortowane zmieniło twój dyktat w listę krotek

np. powiedz, że twój dykt: {'a':1,'c':3,'b':2} posortowany zamienia go w listę:

[('a',1),('b',2),('c',3)]

więc kiedy faktycznie wykonasz iterację po liście, otrzymasz (w tym przykładzie) krotkę złożoną z łańcucha i liczby całkowitej, ale przynajmniej będziesz w stanie iterować po niej.


2

Zakładając, że używasz CPython 2.x i masz duży słownik mydict, to użycie sortowania (mydict) będzie wolne, ponieważ sortowane tworzy posortowaną listę kluczy mydict.

W takim przypadku możesz zajrzeć do mojego pakietu uporządkowanego, który zawiera implementację C sorteddictw C. Szczególnie, jeśli musisz wielokrotnie przeglądać posortowaną listę kluczy na różnych etapach (tj. Liczbie elementów) życia słowników.

http://anthon.home.xs4all.nl/Python/ordereddict/

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.