Ітерація над словниками за допомогою циклів 'for'


3138

Мене трохи спантеличив наступний код:

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

Чого я не розумію, це keyпорція. Як Python розпізнає, що йому потрібно лише прочитати ключ зі словника? Є keyспеціальне слово в Python? Або це просто змінна?


3
Перш ніж опублікувати нову відповідь, подумайте, що на це питання вже є 10+ відповідей. Будь ласка, переконайтеся, що ваша відповідь вносить інформацію, яка не входить до наявних відповідей.
janniks

Відповіді:


5392

key - це лише назва змінної.

for key in d:

буде просто перебирати ключі в словнику, а не ключі та значення. Для переходу на ключ і значення можна скористатись наступним:

Для Python 3.x:

for key, value in d.items():

Для Python 2.x:

for key, value in d.iteritems():

Щоб перевірити на собі, змініть слово keyна poop.

У Python 3.x iteritems()його замінили просто items(), що повертає схожий на набір диктант, як, iteritems()але ще краще. Це також доступно в 2.7 viewitems().

Операція items()буде працювати як для 2, так і для 3, але через 2 вона поверне список (key, value)пар словника , який не відображатиме зміни в диктаті, що відбудуться після items()дзвінка. Якщо ви хочете, щоб поведінка 2.x в 3.x, ви можете зателефонувати list(d.items()).


158
Додавання непоміченої причини не отримати доступ до такого значення: d [key] всередині циклу for for призводить до повторного хешування ключа (щоб отримати значення). Коли словник великий, цей додатковий хеш додасть до загального часу. Про це йдеться у технічній бесіді
Реймонда Хеттінгера

27
Можливо, має сенс згадати, що предмети будуть повторені в непередбачуваному порядку і sortedнеобхідні для її стабілізації.
юг

5
@HarisankarKrishnaSwamy, яка альтернатива?
JoeyC

3
@yugr Чому ти це кажеш? Документи кажуть Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…
Geza Turi

4
@yugr З Python 3.7 словники впорядковані вставкою, і це мовна особливість. Див stackoverflow.com/a/39980744/9428564
Aimery

432

Справа не в тому, що ключ є спеціальним словом, але те, що словники реалізують протокол ітератора. Ви можете це зробити у своєму класі, наприклад, перегляньте це запитання щодо створення ітераторів класу.

Що стосується словників, він реалізується на рівні С. Деталі доступні в PEP 234 . Зокрема, розділ під назвою "Ітератори словника":

  • Словники реалізують слот tp_iter, який повертає ефективний ітератор, який повторюється над ключами словника. [...] Це означає, що ми можемо писати

    for k in dict: ...

    що еквівалентно, але набагато швидше, ніж

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

    до тих пір, поки обмеження щодо модифікацій словника (або циклу, або іншого потоку) не будуть порушені.

  • Додайте методи до словників, які явно повертають різні види ітераторів:

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

    Це означає, що for x in dictце скорочення for x in dict.iterkeys().

В Python 3 dict.iterkeys(), dict.itervalues()і dict.iteritems()більше не підтримується. Використовуйте dict.keys(), dict.values()і dict.items()замість цього.


207

Ітерація через ітерацію за dictдопомогою її клавіш не в конкретному порядку, як ви бачите тут:

Редагувати: (У Python3.6 це вже не так , але зауважте, що це ще не гарантована поведінка)

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

Для вашого прикладу краще використовувати dict.items():

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

Це дає вам список кортежів. Коли ви перетинаєте їх так, кожен кортеж розпаковується автоматично kта vавтоматично:

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

Використання kі в vякості імен змінних , коли цикл більше dictє досить поширеним явищем , якщо тіло циклу складає всього кілька рядків. Для складніших циклів може бути корисним використання більш описових імен:

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

Це гарна ідея ввійти в звичку використовувати рядки формату:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))

11
З примітки до випуску Python 3.7: "Природа збереження порядку вставки об'єктів dict тепер є офіційною частиною специфікації мови Python".
Григорій Ареній

86

key просто змінна.

Для Python2.X :

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

... або краще,

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

Для Python3.X :

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

63

Коли ви перебираєте через словники за допомогою for .. in ..-синтаксису, він завжди переходить до клавіш (значення доступні за допомогою dictionary[key]).

Для повторення пар ключових значень використовуйте в Python 2 for k,v in s.iteritems()та Python 3 for k,v in s.items().


38
Зауважте, що для Python 3 це items()замістьiteritems()
Андреас Фестер

32

Це дуже розповсюджена ідіома петлі. inє оператором. Про те, коли використовувати for key in dictта коли це повинно бути for key in dict.keys()переглянуто статті Idiomatic Python Девіда Гудгера (заархівована копія) .


Коли я читав ці розділи про in, частина оператора - це те, де ви перевіряєте наявність . Можливо, краще видаліть цю in is an operatorінформацію.
Вовк

19

Ітерація над словниками за допомогою циклів 'for'

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

Як Python розпізнає, що йому потрібно лише прочитати ключ зі словника? Чи є ключовим словом спеціальне в Python? Або це просто змінна?

Це не просто forпетлі. Тут важливе слово - "повторення".

Словник - це відображення ключів до значень:

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

Кожен раз, коли ми повторюємо його, ми повторюємо клавіші. Ім'я змінної keyпризначене лише для опису - і цілком підходить для цієї мети.

Це відбувається в розумінні списку:

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

Це відбувається, коли ми передаємо словник до списку (або будь-якого іншого об'єкта типу колекції):

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

Спосіб ітерації Python в контексті, де це потрібно, він викликає __iter__метод об'єкта (в даному випадку словник), який повертає ітератор (в даному випадку об'єкт keyiterator):

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

Ми не повинні використовувати ці спеціальні методи самостійно, натомість використовувати відповідну вбудовану функцію, щоб викликати її iter:

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

Ітератори мають __next__метод - але ми називаємо це функцією вбудованого 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

Коли ітератор вичерпується, він підвищується StopIteration. Ось як Python знає, щоб вийти з forциклу, чи розуміння списку, або вираз генератора, або будь-який інший ітераційний контекст. Після того, як ітератор піднімає StopIterationйого, він завжди підніме його - якщо ви хочете повторити повтор, вам потрібен новий.

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

Повернення до диктів

Ми бачили дикти, що повторюються в багатьох контекстах. Що ми бачили, це те, що кожного разу, коли ми повторюємо дік, ми отримуємо ключі. Повернутися до початкового прикладу:

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

Якщо ми змінимо ім’я змінної, ми все одно отримаємо ключі. Давайте спробуємо:

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

Якщо ми хочемо перебрати значення, нам потрібно використовувати .valuesметод диктів, або для обох разом .items:

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

У наведеному прикладі було б ефективніше переглядати такі елементи:

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

Але в академічних цілях приклад питання просто чудовий.


17

У мене є випадок використання, коли мені доводиться перебирати через дікт, щоб отримати ключ, пару значень, а також індекс, що вказує, де я є. Ось як я це роблю:

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

Зауважте, що в дужках навколо ключа, значення є важливим, без дужок ви отримуєте ValueError "недостатньо значень для розпакування".


1
Наскільки це стосується питання?
jorijnsmit

8

Ви можете перевірити реалізацію CPython's dicttypeна GitHub. Це підпис методу, який реалізує ітератор dict:

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

CPython dictobject.c


4

Щоб перебрати клавіші, це повільніше, але краще використовувати my_dict.keys(). Якщо ви намагалися зробити щось подібне:

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

це створило б помилку під час виконання програми, оскільки ви змінюєте клавіші під час роботи програми. Якщо ви абсолютно налаштовані на скорочення часу, використовуйте for key in my_dictшлях, але вас попередили;).


2

Це надрукує вихід у сортованому порядку за значеннями у порядку зростання.

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)

Вихід:

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.