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 aRuntimeError
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. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s 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 dict
s and set
s 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 dict
s 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
Saving a Numpy Array as an Image
Perform Commands Over Ssh with Python
Execute Code When Django Starts Once Only
How to Get a Directory Listing Sorted by Creation Date in Python
Hexadecimal String to Byte Array in Python
Deprecationwarning: Executable_Path Has Been Deprecated Selenium Python
Why Does Random.Shuffle Return None
Python Subprocess Get Children's Output to File and Terminal
Getting the Class Name of an Instance
What Is a Cross-Platform Way to Get the Home Directory
Apply VS Transform on a Group Object
Django Media_Url and Media_Root
Checking Whether a String Starts with Xxxx