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

<p>я немного озадачен следующим кодом:</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} for key in d: print key, 'corresponds to', d[key] </code></pre><p>что я не понимаю, так это часть <code>key</code>. как python распознает, что ему нужно только прочитать ключ из словаря? является ли <code>key</code> специальным словом в python? или это просто переменная?</p>

<p><code>key</code> это просто имя переменной.</p><pre><code>for key in d: </code></pre><p>будет просто перебирать ключи в словаре, а не ключи и значения. чтобы перебрать ключ и значение, вы можете использовать следующее:</p><p>для python 2.x:</p><pre><code>for key, value in d.iteritems(): </code></pre><p>для python 3.x:</p><pre><code>for key, value in d.items(): </code></pre><p>чтобы проверить себя, измените слово <code>key</code> на <code>poop</code>.</p><p>для python 3.x <code>iteritems()</code> была заменена просто <code>items()</code>, которая возвращает представление, похожее на набор, поддерживаемое dict, как <code>iteritems()</code>, но даже лучше. это также доступно в 2.7 как <code>viewitems()</code>.</p><p>операция <code>items()</code> будет работать как для 2, так и для 3, но в 2 она вернет список пар словаря <code>(key, value)</code>, которые не будут отражать изменения в dict, которые происходят после вызова <code>items()</code>. если вы хотите поведение 2.x в 3.x, вы можете вызвать <code>list(d.items())</code>.</p>

<p>дело не в том, что ключ - это специальное слово, а в том, что словари реализуют протокол итератора. вы можете сделать это в своем классе, например смотрите <a href="https://stackoverflow.com/questions/19151/build-a-basic-python-iterator/24377#24377">этот вопрос</a> о том, как создавать итераторы классов.</p><p>в случае словарей это реализовано на уровне c. подробности доступны в <a href="http://www.python.org/dev/peps/pep-0234/">pep 234</a> . в частности, раздел под названием "словарь итераторов":</p><blockquote> <ul> <li> <p>словари реализуют слот tp\_iter, который возвращает эффективный итератор, который перебирает ключи словаря. [...] это означает, что мы можем написать</p> <pre><code>for k in dict: ... </code></pre> <p>что эквивалентно, но гораздо быстрее, чем</p> <pre><code>for k in dict.keys(): ... </code></pre> <p>до тех пор, пока ограничение на модификации словаря (либо циклом, либо другим потоком) не нарушено.</p> </li> <li> <p>добавьте методы в словари, которые явно возвращают разные типы итераторов:</p> <pre><code>for key in dict.iterkeys(): ... for value in dict.itervalues(): ... for key, value in dict.iteritems(): ... </code></pre> <p>это означает, что <code>for x in dict</code> является сокращением для <code>for x in dict.iterkeys()</code>.</p> </li> </ul> </blockquote><p>в python 3 <code>dict.iterkeys()</code>, <code>dict.itervalues()</code> и <code>dict.iteritems()</code> больше не поддерживаются. вместо этого используйте <code>dict.keys()</code>, <code>dict.values()</code> и <code>dict.items()</code>.</p>

<p>итерации по <code>dict</code> итерируют по его ключам в произвольном порядке, как вы можете видеть здесь:</p><p>правка: (это <strong> больше не имеет место в <a href="https://stackoverflow.com/questions/39980323/dictionaries-are-ordered-in-python-3-6">python3.6</a> </strong>, но обратите внимание, что это <strong> еще не гарантировано </strong> поведение)</p><pre><code>>>> d = {'x': 1, 'y': 2, 'z': 3} >>> list(d) ['y', 'x', 'z'] >>> d.keys() ['y', 'x', 'z'] </code></pre><p>для вашего примера лучше использовать <code>dict.items()</code>:</p><pre><code>>>> d.items() [('y', 2), ('x', 1), ('z', 3)] </code></pre><p>это дает вам список кортежей. когда вы перебираете их так, каждый tuple автоматически распаковывается в <code>k</code> и <code>v</code>:</p><pre><code>for k,v in d.items(): print(k, 'corresponds to', v) </code></pre><p>использование <code>k</code> и <code>v</code> в качестве имен переменных при зацикливании <code>dict</code> довольно распространено, если тело цикла состоит всего из нескольких строк. для более сложных циклов может быть полезно использовать более описательные имена:</p><pre><code>for letter, number in d.items(): print(letter, 'corresponds to', number) </code></pre><p>это хорошая идея, чтобы привыкнуть использовать строки формата:</p><pre><code>for letter, number in d.items(): print('{0} corresponds to {1}'.format(letter, number)) </code></pre>

<p><code>key</code> это просто переменная.</p><p>для <strong>python2.x</strong>:</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} for my\_var in d: print my\_var, 'corresponds to', d[my\_var] </code></pre><p>... или лучше,</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} for the\_key, the\_value in d.iteritems(): print the\_key, 'corresponds to', the\_value </code></pre><p>для <strong>python3.x</strong>:</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} for the\_key, the\_value in d.items(): print(the\_key, 'corresponds to', the\_value) </code></pre>

<p>когда вы перебираете словари, используя синтаксис <code>for .. in ..</code>-, он всегда перебирает ключи (значения доступны с помощью <code>dictionary[key]</code>).</p><p>чтобы перебрать пары ключ-значение, в python 2 используйте <code>for k,v in s.iteritems()</code>, а в python 3 <code>for k,v in s.items()</code>.</p>

<p>это очень распространенная идиома. <code>in</code> является оператором. информацию о том, когда использовать <code>for key in dict</code> и когда это должно быть <code>for key in dict.keys()</code>, см. в статье <a href="https://web.archive.org/web/20180411011411/http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html">david igermatic idiomatic python (архивная копия)</a> .</p>

<p>вы можете использовать это:</p><pre><code>for key,val in d.items(): print key, 'is the key for ', val </code></pre>

<p>у меня есть сценарий использования, где я должен пройти через dict, чтобы получить ключ, пару значений, а также индекс, указывающий, где я нахожусь. вот как я это делаю:</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} for i, (key, value) in enumerate(d.items()): print(i, key, value) </code></pre><p>обратите внимание, что круглые скобки вокруг ключа, значения важны, без скобок вы получаете valueerror "недостаточно значений для распаковки".</p>

<p>как бы то ни было, сегодня python 2.6 и 2.7, а также 3.x в моем окне хорошо работают с <code>items()</code>:</p><pre><code>z = {0: 'a', 1: 'b'} for k, v in z.items(): print(v, k) </code></pre>

<blockquote> <h2>перебор словарей с использованием циклов for</h2> <pre><code>d = {'x': 1, 'y': 2, 'z': 3} for key in d: ... </code></pre> <p>как python распознает, что ему нужно только прочитать ключ из словаря? является ли ключ специальным словом в python? или это просто переменная?</p> </blockquote><p>это не просто петли <code>for</code>. важным словом здесь является "итерация".</p><p>словарь - это отображение ключей на значения:</p><pre><code>d = {'x': 1, 'y': 2, 'z': 3} </code></pre><p>каждый раз, когда мы перебираем его, мы перебираем ключи. имя переменной <code>key</code> предназначено только для наглядности и вполне подходит для этой цели.</p><p>это происходит в понимании списка:</p><pre><code>>>> [k for k in d] ['x', 'y', 'z'] </code></pre><p>это происходит, когда мы передаем словарь в список (или любой другой объект типа коллекции):</p><pre><code>>>> list(d) ['x', 'y', 'z'] </code></pre><p>способ итерации python заключается в том, что в контексте, где это необходимо, он вызывает метод <code>\_\_iter\_\_</code> объекта (в данном случае словарь), который возвращает итератор (в данном случае объект keyiterator):</p><pre><code>>>> d.\_\_iter\_\_() <dict\_keyiterator object at 0x7fb1747bee08> </code></pre><p>мы не должны сами использовать эти специальные методы, вместо этого использовать соответствующую встроенную функцию для ее вызова, <code>iter</code>:</p><pre><code>>>> key\_iterator = iter(d) >>> key\_iterator <dict\_keyiterator object at 0x7fb172fa9188> </code></pre><p>итераторы имеют метод <code>\_\_next\_\_</code>, но мы вызываем его с помощью встроенной функции <code>next</code>:</p><pre><code>>>> 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 </code></pre><p>когда итератор исчерпан, он вызывает <code>stopiteration</code>. вот как python знает, что нужно выйти из цикла <code>for</code>, или из списка, или из выражения генератора, или из любого другого итеративного контекста. как только итератор вызывает <code>stopiteration</code>, он всегда будет вызывать его - если вы хотите повторить итерацию, вам нужен новый.</p><pre><code>>>> list(key\_iterator) [] >>> new\_key\_iterator = iter(d) >>> list(new\_key\_iterator) ['x', 'y', 'z'] </code></pre><h2>возвращаясь к диктату</h2><p>мы видели повторения диктов во многих контекстах. что мы видели, так это то, что каждый раз, когда мы перебираем слова, мы получаем ключи. вернуться к исходному примеру:</p><blockquote> <pre><code>d = {'x': 1, 'y': 2, 'z': 3} for key in d: </code></pre></blockquote><p>если мы изменим имя переменной, мы все равно получим ключи. давай попробуем:</p><pre><code>>>> for each\_key in d: ... print(each\_key, '=>', d[each\_key]) ... x => 1 y => 2 z => 3 </code></pre><p>если мы хотим перебрать значения, нам нужно использовать метод dicts <code>.values</code> или для обоих вместе, <code>.items</code>:</p><pre><code>>>> list(d.values()) [1, 2, 3] >>> list(d.items()) [('x', 1), ('y', 2), ('z', 3)] </code></pre><p>в приведенном примере было бы более эффективно перебирать элементы вроде этого:</p><pre><code>for a\_key, corresponding\_value in d.items(): print(a\_key, corresponding\_value) </code></pre><p>но для академических целей пример вопроса просто отлично.</p>

<p>вы можете проверить реализацию <code>dicttype</code> cpython на github. это сигнатура метода, который реализует итератор dict:</p><pre><code>\_pydict\_next(pyobject *op, py\_ssize\_t *ppos, pyobject **pkey, pyobject **pvalue, py\_hash\_t *phash) </code></pre><p> <a href="https://github.com/python/cpython/blob/b2e5794870eb4728ddfaafc0f79a40299576434f/objects/dictobject.c">cpython dictobject.c</a> </p>

<p>для итерации ключей медленнее, но лучше использовать <code>my\_dict.keys()</code>. если вы пытались сделать что-то вроде этого:</p><pre><code>for key in my\_dict: my\_dict[key+"-1"] = my\_dict[key]-1 </code></pre><p>это приведет к ошибке во время выполнения, потому что вы меняете ключи во время работы программы. если вы абсолютно настроены на сокращение времени, используйте способ <code>for key in my\_dict</code>, но вы были предупреждены;).</p>

<p>для ключа в my\_dict фактически равно для ключа в my\_dict.keys (). поэтому, если вы хотите получить значения dict, вы можете попробовать два метода.</p><p>один:</p><pre><code>for value in my\_dict.values(): print(value) </code></pre><p>два:</p><pre><code>for key in my\_dict: print(my\_dict[key]) </code></pre>

<p><strong>распечатка предложения для длинных значений</strong><br> если у вас есть длинные строки в значениях, вот быстрый способ распечатать это в (более?) читаемом формате:<br> <code>for key, value in my\_dictionary.iteritems(): print(key) print("---------") print(value) print("\n")</code></p>