Обход словарей в цикле

Словари являются итерируемы объектами, но порядок извлечения элементов из них может быть произвольным. Более того обход элементов словарей первоначально может показаться очень странным, так как вместо элементов вы увидите только их ключи, но не их значения:

>>> for i in a:
...     print(i)
...
b
d
e
f
z
w

Получить доступ к элементав цикле можно следующим образом:

>>> for i in a:
...     print(a[i])
...
22
-555
55
-777
-999
-888

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

>>> for i in a:
...     print(i, a[i])
...
b 22
d -555
e 55
f -777
z -999
w -888

С другой стороны, лучше воспользоваться методом .items(), который возвращает итерируемый объект dict_items с кортежами из пар "ключ-значение":

>>> a.items()
dict_items([('b', 22), ('d', -555), ('e', 55), ('f', -777), ('z', -999), ('w', -888)])

Используя данный объект мы можем обойти словарь в цикле двумя другими способами. Первый способ основан на том что переменной i присваиваются кортежи из a.items():

>>> for i in a.items():
...     print(i[0], i[1])
...
b 22
d -555
e 55
f -777
z -999
w -888

Второй способ, основан на том что кортежи из a.items() распаковываются в переменные key и value:

>>> for key, value in a.items():    # 
...     print(key, value)
...
b 22
d -555
e 55
f -777
z -999
w -888

Метод .items() возвращает представление словаря - итерируемый и доступный только для чтения. Помимо него существуют еще два метода, которые возвращают представления: .keys() - возвращает представление ключей и .values() - возвращает представление значений. Так что появляется еще два способа обойти ключи и значения в цикле, которые, кстати, являются более "читабельными":

>>> for i in a.keys():
...      print(i)
...
b
d
e
f
z
w
>>>
>>> for i in a.values():
...      print(i)
...
22
-555
55
-777
-999
-888

Представления словарей это весьма любопытные объекты. Они доступны только для чтения, но при этом поддерживают некоторые операции, специфичные для словарей. Что бы продемонстрировать это, сначала создадим подходящие словари:

>>> a = dict(zip(('ABCxyz'), range(6)))
>>> a
{'A': 0, 'B': 1, 'C': 2, 'x': 3, 'y': 4, 'z': 5}
>>>
>>> b = dict(zip(('DEFxyz'), range(3, 10)))
>>> b
{'D': 3, 'E': 4, 'F': 5, 'x': 6, 'y': 7, 'z': 8}

А затем создадим их представления и посмотрим на их содержимое:

>>> a_view = a.keys()
>>> a_view
dict_keys(['A', 'B', 'C', 'x', 'y', 'z'])
>>>
>>> b_view = b.keys()
>>> b_view
dict_keys(['D', 'E', 'F', 'x', 'y', 'z'])

Теперь мы можем выполнять над a_view и b_view операции так, словно они являются множествами:

>>> a_view & b_view    # пересечение множеств ключей словарей
{'x', 'z', 'y'}
>>>
>>> a_view | b_view    # объединение множеств ключей
{'F', 'C', 'x', 'A', 'z', 'B', 'y', 'E', 'D'}
>>>
>>> a_view - b_view    # разность множеств
{'C', 'A', 'B'}
>>>
>>> a_view ^ b_view    # симметрическая разность (строгая дизъюнкция)
{'F', 'C', 'A', 'B', 'E', 'D'}

Использование таких представлений упрощает итерирование нескольких словарей. Например, если нам нужны значения словарей ключи которых совпадают, то можно выполнить следующий трюк:

>>> for i in a_view & b_view:
...     print(i, a[i], b[i])
...
x 3 6
z 5 8
y 4 7

Удивительно, но тоже самое касается не только представлений ключей, но и представлений пар "ключ-значение":

>>> a = dict(zip(('ABCxyz'), range(6)))
>>> b = dict(zip(('DEFxyz'), [5, 4, 3, 3, 4, 5]))
>>>
>>> a.items() & b.items()
{('y', 4), ('x', 3), ('z', 5)}
>>>
>>> a.items() ^ b.items()
{('F', 3), ('C', 2), ('D', 5), ('E', 4), ('A', 0), ('B', 1)}

Это, например, можно использовать для того что бы извлеч те элементы, ключи которых не присутствуют в a и b одновременно:

>>> for i in a.items() ^ b.items():
...     print(i[1])
...
3
2
5
4
0
1