Python's Dictionary Implementation: Being All Things to All People > Iterations and Dynamic Changes

18.5. Iterations and Dynamic Changes

A common use case is looping through the contents of a dictionary. The keys(), values(), and items() methods return lists containing all of the keys, values, or key/value pairs in the dictionary. To conserve memory, the user can call the iterkeys(), itervalues(), and iteritems() methods instead; they return an iterator object that returns elements one by one. But when these iterators are used, Python has to forbid any statement that adds or deletes an entry in the dictionary during the loop.

This restriction turns out to be fairly easy to enforce. The iterator records the number of items in the dictionary when an iter*() method is first called. If the size changes, the iterator raises a RuntimeError exception with the message dictionary changed size during iteration.

One special case that modifies a dictionary while looping over it is code that assigns a new value for the same key:

	for k, v in d.iteritems():
	    d[k] = d[k] + 1

It's convenient to avoid raising a RuntimeError exception during such operations. Therefore, the C function that handles dictionary insertion, PyDict_SetItem(), guarantees not to resize the dictionary if it inserts a key that's already present. The lookdict() and lookdict_ string search functions support this feature by the way they report failure (not finding the searched-for key): on failure, they return a pointer to the empty slot where the searched-for key would have been stored. This makes it easy for PyDict_SetItem to store the new value in the returned slot, which is either an empty slot or a slot known to be occupied by the same key. When the new value is recorded in a slot already occupied by the same key, as in d[k]=d[k]+1, the dictionary's size isn't checked for a possible resize operation, and the RuntimeError is avoided. Code such as the previous example therefore runs without an exception.