Modifying a Python Dict While Iterating Over It

Modifying a Python dict while iterating over it

It is explicitly mentioned on the Python doc page (for Python 2.7) that

Using iteritems() while adding or deleting entries in the dictionary may raise a RuntimeError or fail to iterate over all entries.

Similarly for Python 3.

The same holds for iter(d), d.iterkeys() and d.itervalues(), and I'll go as far as saying that it does for for k, v in d.items(): (I can't remember exactly what for does, but I would not be surprised if the implementation called iter(d)).

How to change a dictionary while iterating over its keys while iterating over a list

You should not change the size of a dictionary while iterating over a view of that dictionary. You can, instead, construct a new dictionary and then print whatever you like. For example:

d = {'this': '1', 'is': '2', 'a': '3', 'list': '4'}
L = ['A', 'B', 'C', 'D', 'E']

d_new = {k: v for k, v in d.items() if len(k) >= 2}

for word in L:
for key in d_new:
print(word, key)

As described in the docs:

The objects returned by dict.keys(), dict.values() and
dict.items() are view objects. They provide a dynamic view on the
dictionary’s entries, which means that when the dictionary changes,
the view reflects these changes....
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.

Modify list and dictionary during iteration, why does it fail on dict?

I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.

You could use this, for example let's say you wanted to split a list with an even number of elements in half and reverse the 2nd half:

>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])

Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.

By contrast, the behaviour for dicts and sets is totally implementation specific since the iteration order may change depending on the hashing.

You get a RunTimeError with collections.OrderedDict, presumably for consistency with the dict behaviour. I don't think any change in the dict behaviour is likely after Python 3.6 (where dicts are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.

Note that collections.deque also raises a RuntimeError in this case, despite being ordered.

modifying a nested dict while iterating

None of the operations you've described even touch the outer dict. They're completely fine to do.

If you did something like

for key in outer_dict:
outer_dict[key] = something_different

that would touch the outer dict, but it would still be fine. As long as you're not inserting or removing keys in the dict you're iterating over, you won't trigger a rehashing.



Related Topics



Leave a reply



Submit