Перебор словарей с использованием циклов for

3141

Я немного озадачен следующим кодом:

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

То, что я не понимаю, это keyчасть. Как Python распознает, что ему нужно только прочитать ключ из словаря? Это keyособое слово в Python? Или это просто переменная?

TopChef
источник
4
Прежде чем опубликовать новый ответ, учтите, что на этот вопрос уже есть более 10 ответов. Пожалуйста, убедитесь, что ваш ответ содержит информацию, которой нет среди существующих ответов.
Янник

Ответы:

5395

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(), что возвращает представление, похожее на набор, поддерживаемое dict, как, iteritems()но даже лучше. Это также доступно в 2.7 как viewitems().

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

sberry
источник
158
Добавление пропущенной причины отказа от доступа к значению, подобному следующему: d [ключ] внутри цикла for приводит к повторному хешированию ключа (для получения значения). Когда словарь большой, этот дополнительный хеш добавит к общему времени. Это обсуждается в техническом
докладе
27
Возможно, имеет смысл упомянуть, что элементы будут повторяться в непредсказуемом порядке и sortedнеобходимы для его стабилизации.
Югр
5
@HarisankarKrishnaSwamy какая альтернатива?
JoeyC
3
@yugr Почему ты так говоришь? В документации сказано Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…
Геза Тури
4
@yugr В Python 3.7 словари упорядочены по типу вставки, и это особенность языка. См. Stackoverflow.com/a/39980744/9428564
Aimery
433

Дело не в том, что ключ - это особое слово, а в том, что словари реализуют протокол итератора. Вы можете сделать это в своем классе, например, посмотреть этот вопрос о том, как создавать итераторы класса.

В случае словарей это реализовано на уровне C. Детали доступны в 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качестве имен переменных при зацикливании над a 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)
ssoler
источник
63

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

Для перебора пар ключ-значение в Python 2 for k,v in s.iteritems()и в Python 3 for k,v in s.items().

Александр Гесслер
источник
38
Обратите внимание, что для Python 3 это items()вместоiteritems()
Andreas Fester
32

Это очень распространенная идиома. inэто оператор. О том, когда for key in dictи когда его использовать, можно прочитать в for key in dict.keys()статье «Идиоматический Python» Дэвида Гуджера (архивная копия) .

chryss
источник
Как я читал в этих разделах 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метод dicts или для обоих вместе .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

У меня есть сценарий использования, где я должен пройти через dict, чтобы получить ключ, пару значений, а также индекс, указывающий, где я нахожусь. Вот как я это делаю:

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

Обратите внимание, что круглые скобки вокруг ключа, значения важны, без скобок вы получите ValueError «недостаточно значений для распаковки».

jdhao
источник
1
Какое отношение это имеет к вопросу?
jorijnsmit
8

Вы можете проверить реализацию CPython 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)

Вывод:

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

Амар Кумар
источник