[python] Iterating over dictionaries using 'for' loops

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?

It's not just for loops. The important word here is "iterating".

A dictionary is a mapping of keys to values:

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

Any time we iterate over it, we iterate over the keys. The variable name key is only intended to be descriptive - and it is quite apt for the purpose.

This happens in a list comprehension:

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

It happens when we pass the dictionary to list (or any other collection type object):

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

The way Python iterates is, in a context where it needs to, it calls the __iter__ method of the object (in this case the dictionary) which returns an iterator (in this case, a keyiterator object):

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

We shouldn't use these special methods ourselves, instead, use the respective builtin function to call it, iter:

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

Iterators have a __next__ method - but we call it with the builtin function, 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

When an iterator is exhausted, it raises StopIteration. This is how Python knows to exit a for loop, or a list comprehension, or a generator expression, or any other iterative context. Once an iterator raises StopIteration it will always raise it - if you want to iterate again, you need a new one.

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

Returning to dicts

We've seen dicts iterating in many contexts. What we've seen is that any time we iterate over a dict, we get the keys. Back to the original example:

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

If we change the variable name, we still get the keys. Let's try it:

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

If we want to iterate over the values, we need to use the .values method of dicts, or for both together, .items:

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

In the example given, it would be more efficient to iterate over the items like this:

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

But for academic purposes, the question's example is just fine.