Iterowanie po słownikach za pomocą pętli „for”

3135

Jestem trochę zdziwiony następującym kodem:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print key, 'corresponds to', d[key]

To, czego nie rozumiem, to keyporcja. Jak Python rozpoznaje, że wystarczy tylko odczytać klucz ze słownika? Czy keyw Pythonie jest specjalne słowo? Czy jest to po prostu zmienna?

TopChef
źródło
3
Zanim opublikujesz nową odpowiedź, zastanów się, że na to pytanie jest już ponad 10 odpowiedzi. Upewnij się, że twoja odpowiedź zawiera informacje, których nie ma wśród odpowiedzi istniejących.
janniks

Odpowiedzi:

5388

key to tylko nazwa zmiennej.

for key in d:

po prostu zapętli klawisze w słowniku, a nie klucze i wartości. Aby zapętlić zarówno klucz, jak i wartość, możesz użyć:

W przypadku Python 3.x:

for key, value in d.items():

W przypadku Python 2.x:

for key, value in d.iteritems():

Aby sprawdzić sam, zmień słowo keyna poop.

W Pythonie 3.x iteritems()został zastąpiony przez po prostu items(), który zwraca widok podobny do zestawu, wspierany przez dyktat, iteritems()ale jeszcze lepszy. Jest to również dostępne w wersji 2.7 jako viewitems().

Operacja items()będzie działać zarówno dla 2, jak i 3, ale w 2 zwróci listę (key, value)par słownika , która nie będzie odzwierciedlała zmian w dyktandzie, które nastąpiły po items()wywołaniu. Jeśli chcesz zachować 2.x w 3.x, możesz zadzwonić list(d.items()).

jagoda
źródło
157
Dodanie przeoczonego powodu, aby nie uzyskiwać dostępu do wartości w ten sposób: d [klawisz] wewnątrz pętli for powoduje, że klucz jest ponownie mieszany (aby uzyskać wartość). Gdy słownik jest duży, ten dodatkowy skrót doda do ogólnego czasu. Jest to omówione w dyskusji technologicznej Raymonda Hettingera youtube.com/watch?v=anrOzOapJ2E
quiet_penguin
27
Warto wspomnieć, że przedmioty będą powtarzane w nieprzewidywalnej kolejności i sortedsą potrzebne do ich ustabilizowania.
'18
5
@HarisankarKrishnaSwamy jaka jest alternatywa?
JoeyC,
3
@yugr Dlaczego tak mówisz? Dokumenty mówią Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…
Geza Turi,
4
@yugr W Pythonie 3.7 słowniki są uporządkowane według wstawiania i jest to funkcja językowa. Zobacz stackoverflow.com/a/39980744/9428564
Aimery
432

Nie chodzi o to, że klucz jest słowem specjalnym, ale o to, że słowniki implementują protokół iteratora. Możesz to zrobić w swojej klasie, np. Zobacz to pytanie, jak zbudować iteratory klas.

W przypadku słowników jest on implementowany na poziomie C. Szczegóły są dostępne w PEP 234 . W szczególności sekcja zatytułowana „Iteratory słownikowe”:

  • Słowniki implementują gniazdo tp_iter, które zwraca wydajny iterator, który iteruje po klawiszach słownika. [...] Oznacza to, że możemy pisać

    for k in dict: ...

    co jest równoważne, ale znacznie szybsze niż

    for k in dict.keys(): ...

    dopóki nie zostaną naruszone ograniczenia dotyczące modyfikacji słownika (przez pętlę lub przez inny wątek).

  • Dodaj metody do słowników, które jawnie zwracają różne rodzaje iteratorów:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...
    

    Oznacza to, że for x in dictjest to skrót for x in dict.iterkeys().

W Pythonie 3 dict.iterkeys(), dict.itervalues()i dict.iteritems()nie są już obsługiwane. Zastosowanie dict.keys(), dict.values()a dict.items()zamiast tego.

ars
źródło
207

Iteracja po dictiteracji przez klucze w żadnej określonej kolejności, jak widać tutaj:

Edycja: (Nie dotyczy to już Python3.6 , ale zauważ, że nie jest to jeszcze zagwarantowane zachowanie)

>>> d = {'x': 1, 'y': 2, 'z': 3} 
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

Na przykład lepiej jest użyć dict.items():

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

To daje listę krotek. Gdy zapętlisz je w ten sposób, każda krotka jest rozpakowywana ki vautomatycznie:

for k,v in d.items():
    print(k, 'corresponds to', v)

Używanie ki vjako nazw zmiennych podczas zapętlania nad a dictjest dość powszechne, jeśli treść pętli składa się tylko z kilku linii. W przypadku bardziej skomplikowanych pętli dobrym pomysłem może być użycie bardziej opisowych nazw:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

Warto przyzwyczaić się do używania ciągów formatu:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))
John La Rooy
źródło
11
Z informacji o wydaniu Python 3.7: „Zachowanie kolejności wstawiania obiektów dict jest teraz oficjalną częścią specyfikacji języka Python”.
Gregory Arenius
86

key jest po prostu zmienną.

W przypadku Python2.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

... albo lepiej,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

W przypadku Python3.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)
ssoler
źródło
62

Podczas iteracji przez słowniki za pomocą opcji for .. in ..-syntax zawsze iteruje się po klawiszach (wartości są dostępne za pomocą dictionary[key]).

Aby iterować po parach klucz-wartość, w Pythonie 2 for k,v in s.iteritems()i Pythonie 3 for k,v in s.items().

Alexander Gessler
źródło
38
Zauważ, że w Pythonie 3 jest to items()zamiastiteritems()
Andreas Fester
19

Iterowanie po słownikach za pomocą pętli „for”

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

Jak Python rozpoznaje, że wystarczy tylko odczytać klucz ze słownika? Czy klucz to specjalne słowo w Pythonie? Czy jest to po prostu zmienna?

To nie tylko forpętle. Ważnym słowem jest tutaj „iteracja”.

Słownik to mapowanie kluczy na wartości:

d = {'x': 1, 'y': 2, 'z': 3} 

Za każdym razem, gdy wykonujemy iterację, iterujemy po klawiszach. Nazwa zmiennej keyma jedynie charakter opisowy - i jest całkiem trafna do tego celu.

Dzieje się tak w przypadku zrozumienia listy:

>>> [k for k in d]
['x', 'y', 'z']

Dzieje się tak, gdy przekazujemy słownik do listy (lub dowolnego innego obiektu typu kolekcji):

>>> list(d)
['x', 'y', 'z']

Sposób, w jaki Python wykonuje iterację, w kontekście, w którym musi, wywołuje __iter__metodę obiektu (w tym przypadku słownika), który zwraca iterator (w tym przypadku obiekt keyiteratora):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

Nie powinniśmy sami korzystać z tych specjalnych metod, zamiast tego użyj odpowiedniej funkcji wbudowanej, aby ją wywołać iter:

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

Iteratory mają __next__metodę - ale nazywamy ją wbudowaną funkcją next:

>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Kiedy iterator jest wyczerpany, podnosi się StopIteration. W ten sposób Python wie, jak wyjść z forpętli, ze zrozumienia listy, wyrażenia generatora lub dowolnego innego kontekstu iteracyjnego. Gdy iterator podniesie StopIteration, zawsze go podniesie - jeśli chcesz powtórzyć iterację, potrzebujesz nowego.

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

Wracając do dykt

Widzieliśmy powtarzające się dyktaty w wielu kontekstach. Zauważyliśmy, że za każdym razem, gdy powtarzamy dyktando, dostajemy klucze. Powrót do oryginalnego przykładu:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

Jeśli zmienimy nazwę zmiennej, nadal otrzymamy klucze. Spróbujmy:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

Jeśli chcemy iterować po wartościach, musimy zastosować .valuesmetodę dyktowania lub dla obu razem .items:

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

W podanym przykładzie bardziej efektywne byłoby iterowanie takich elementów:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

Ale dla celów akademickich przykład tego pytania jest w porządku.

Aaron Hall
źródło
17

Mam przypadek użycia, w którym muszę iterować po słowie, aby uzyskać klucz, parę wartości, a także indeks wskazujący, gdzie jestem. Tak to robię:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

Zauważ, że nawiasy wokół klucza, wartość jest ważna, bez nawiasów otrzymasz błąd ValueError „za mało wartości do rozpakowania”.

jdhao
źródło
1
Jak to się ma do pytania?
jorijnsmit
8

Możesz sprawdzić implementację CPython dicttypena GitHub. To jest podpis metody, która implementuje iterator dict:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython dictobject.c

Ankur Agarwal
źródło
4

Aby iterować po klawiszach, jest wolniejszy, ale lepiej go używać my_dict.keys(). Jeśli próbowałeś zrobić coś takiego:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

spowodowałoby to błąd w czasie wykonywania, ponieważ zmieniasz klucze podczas działania programu. Jeśli absolutnie zależy Ci na skróceniu czasu, skorzystaj z for key in my_dictdrogi, ale zostałeś ostrzeżony;).

Neil Chowdhury o_O
źródło
2

Spowoduje to wydrukowanie wyniku w porządku posortowanym według wartości w porządku rosnącym.

d = {'x': 3, 'y': 1, 'z': 2}
def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

Wynik:

wprowadź opis zdjęcia tutaj

Amar Kumar
źródło