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

3620

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

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

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

3
  • 23
    Прежде чем опубликовать новый ответ, подумайте, что на этот вопрос уже есть 10+ ответов. Пожалуйста, убедитесь, что ваш ответ содержит информацию, которой нет среди существующих ответов. janniks 3 фев '20 в 11:51
  • Как мы знаем, в Словаре Python как набор пар ключ: значение с требованием, чтобы ключи были уникальными (в пределах одного словаря). Пара скобок создает пустой словарь: {}. Размещение разделенного запятыми списка пар ключ: значение в фигурных скобках добавляет в словарь исходные пары ключ: значение. Нет, ключ - это не особое слово в Python. Здесь ключ - это просто имя переменной. Raksha Saini 10 янв в 4:26
  • вы пробовали for key, value in d.items()::? Charlie Parker 16 мар в 22:11
6024

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

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

11
  • 206
    Добавление пропущенной причины не обращаться к значению, например: d [key] внутри цикла for, приводит к повторному хешированию ключа (для получения значения). Когда словарь большой, этот дополнительный хеш добавляется к общему времени. Это обсуждается в техническом выступлении Раймонда Хеттингера youtube.com/watch?v=anrOzOapJ2Equiet_penguin 28 июля '17 в 12: 432017-07-28 12:43
  • 36
    Возможно, имеет смысл упомянуть, что элементы будут повторяться в непредсказуемом порядке, и sortedэто необходимо для его стабилизации. yugr 25 авг.
  • 6
    @HarisankarKrishnaSwamy какая альтернатива? JoeyC 8 ноя '18 в 4:45
  • 9
    @yugr Почему ты так говоришь? В документах говорится Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…Geza Turi 13 июл.
  • 20
    @yugr Начиная с Python 3.7 словари упорядочены по вставке, и это особенность языка. См. Stackoverflow.com/a/39980744/9428564Aimery 9 сен '19 в 14:54
489

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

В случае словарей это реализовано на уровне C. Подробности доступны в PEP 234 . В частности, раздел «Итераторы словаря»:

  • Dictionaries implement a tp_iter slot that returns an efficient iterator that iterates over the keys of the dictionary. [...] This means that we can write

    for k in dict: ...
    

    which is equivalent to, but much faster than

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

    as long as the restriction on modifications to the dictionary (either by the loop or by another thread) are not violated.

  • Add methods to dictionaries that return different kinds of iterators explicitly:

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

    This means that for x in dict is shorthand for for x in dict.iterkeys().

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

0
239

dictКак вы можете видеть здесь, при итерации по ключам в произвольном порядке используются итерации:

(Это уже не так в Python 3.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))
1
  • 20
    Из примечаний к выпуску Python 3.7: «Сохранение порядка вставки объектов dict теперь является официальной частью спецификации языка Python». Gregory Arenius 18 июля '18 в 16: 302018-07-18 16:30
102

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)
0
71

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

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

1
  • 44 год
    Обратите внимание, что для Python 3 это items()вместоiteritems()Andreas Fester 26 марта '15 в 11: 382015-03-26 11:38
39

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

1
27

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

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

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

2
  • 2
    Как это относится к вопросу? jorijnsmit 13 апр '20 в 9:19
  • Мне не нравится использование iздесь. Это означает, что значения повторяются в последовательном порядке, что не такNeuron 25 февраля в 12:55
26

Iterating over dictionaries using 'for' loops

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

How does Python recognize that it needs only to read the key from the dictionary? Is key a special word in Python? Or is it simply a variable?

Это не просто 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']

Возвращаясь к диктату

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

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)

Но для академических целей пример вопроса подойдет.

11

Вы можете проверить реализацию CPython dicttypeна GitHub. Это сигнатура метода, реализующего итератор dict:

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

CPython dictobject.c

8

Это напечатает вывод в отсортированном порядке по значениям в возрастающем порядке.

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)

Выход:

y -> 1
z -> 2
x -> 3
1
  • 1
    Я не думаю, что это был вопрос. Вопрос был о ключе и почему python берет ключи из словаря без опции .items () или .keys (). Joe Ferndz 29 дек.
7

Для перебора ключей это медленнее, но лучше использовать my_dict.keys(). Если вы пытались сделать что-то вроде этого:

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

это приведет к ошибке выполнения, потому что вы меняете ключи во время работы программы. Если вы абсолютно настроены на сокращение времени, используйте for key in my_dictспособ, но вас предупредили.

1
  • 1
    Почему «лучше» использовать my_dict.keys()перебор непосредственно по словарю? Итерация по словарю четко документируется как выдача ключей. Похоже, когда вы ответили на этот вопрос, вы имели в виду Python 2, потому что в Python 3 for key in my_dict.keys()все еще будет та же проблема с изменением размера словаря во время итерации . Martijn Pieters 13 окт.
5

Если вы ищете наглядный и наглядный пример:

cat  = {'name': 'Snowy', 'color': 'White' ,'age': 14}
for key , value in cat.items():
   print(key, ': ', value)

Результат:

name:  Snowy
color:  White
age:  14
0

Перейдем сразу к делу. Если слово «ключ» - это просто переменная, как вы упомянули, то главное, что нужно отметить, это то, что когда вы запускаете цикл FOR по словарю, он проходит только через «ключи» и игнорирует «значения» .

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

скорее попробуйте это:

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

но если вы используете такую ​​функцию, как:

d = {'x': 1, 'y': 2, 'z': 3}
print(d.keys())

в приведенном выше случае «ключи» - это просто не переменная, а функция.