Python - How To Fix Error - Dictionary Changed Size During Iteration

ID : 177

viewed : 105

Tags : PythonPython Dictionary

vote vote

99

This Runtime error occurs when we remove, modify, or add new entries in a dictionary object during iteration. This error occurs when iterating through a dictionary but virtually all iterable objects regardless of the programming language used.

The code snippet below illustrates how this error comes about when iterating through a dictionary and making changes simultaneously.

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }  for x in cars.keys():   cars["color"] = "white" print(x) 

In the code block above, we add a new item to the original dictionary while iterating. This will return a Runtime Error, letting us know that the dictionary size changed during iteration, implying that we cannot modify the dictionary while iterating simultaneously.

Sample Code:

Traceback (most recent call last): File "<string>", line 8, in <module> RuntimeError: dictionary changed size during iteration 

While performing any iteration to an object, both deletion, addition, or modification are considered an alteration and cannot be performed while iterating. The code example below demonstrates that this error will also persist if we modify the dictionary while iterating. Therefore, we will still get the same error if we remove an existing item from a dictionary while iterating.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }  for x in cars.keys():     del cars["model"] print(cars) 

Output:

Traceback (most recent call last): File "<string>", line 8, in <module> RuntimeError: dictionary changed size during iteration 

In Python 3, iterating over a changing object is considered a bad style of writing code and unsafe. Generally, in Programming, we cannot mutate an object while iterating over it simultaneously; this rule extends to iterables such as lists and even arrays.

However, if a function mutates an object, we must make sure that the function only mutates a copy of the original object, leaving the original object intact. This is one of the widely used approaches of making alterations to objects while iterating through them simultaneously.

This is a good practice and the best way of avoiding instances of creating an infinite loop that may eventually lead to memory exhaustion. Several solutions can be used to handle this error, and we will discuss each one here.

Creating a Shallow Copy of the Dictionary

Python provides us with the copy() module that allows us to create a copy of an object with no binding to the original object. This lets us freely modify the copy of the object, leaving the original object intact.

Note that the same cannot be realized by using the assignment operator in Python. Using the assignment operator does not create a copy of the original object but rather a variable that refers to the original object.

Therefore any modifications made to the new object will also affect the original object. New developers often misuse this operator.

Sample Code:

import copy cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021,  }  #creating a shallow copy cars_copy = copy.copy(cars)  for x in cars_copy.keys():     cars["color"] = "black"      print(cars) print(cars_copy) 

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'} {'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021} 

In the sample code provided, we have used the copy module’s copy function to create a dictionary copy that we can freely iterate without affecting the original dictionary. Making changes to a dictionary copy allows us to iterate over the dictionary without encountering an error.

Alternatively, we can use the ** operator that is often referred to as the two asterisk operators to rewrite the code above, as shown below.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }  #creating a shallow copy cars_copy = {**cars}   for x in cars_copy.keys():   cars["color"] = "black"    print(cars) 

The ** operator can take key-value pairs from one dictionary and dump them into another dictionary.

Although the operator is widely used to pass in keyword arguments in Python, we have used the operator to unpack the dictionary and obtain the key-value pairs in the code above. We then create a copy of the dictionary and dump the unpacked values in this new dictionary.

Output:

'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'} 

Deleting a key-value pair from a dictionary is no exception either when performing an iteration and thus should follow a similar approach. Therefore using the same procedure, we will delete the key named model and its value Model S Plaid as shown below.

Sample Code:

import copy cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021,  "color": "black"  }   cars_copy = copy.copy(cars)   for x in cars_copy.keys():     if x == "model":         del cars["model"]     print(cars) 

Output:

{'brand': 'Tesla', 'year': 2021, 'color': 'black'} 

Another solution would be to create a copy of the keys that we can then iterate over while modifying the dictionary. However, this can only work in Python 2 and not Python 3 because when done in Python 3, the keys do not return the iterable.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021,  "color": "black"  }  key_copys = list(cars.keys()) print(key_copys)  for key in list(key_copys):     if cars[key] == "model":         cars.pop("model")          print(cars) 

Sample Output:

['brand', 'model', 'year', 'color'] {'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'} 

Casting Dictionary Items to a List

Since we cannot iterate over the dictionary while making changes, we can instead create a casting list and iterate over the list while making changes to the dictionary. Iterating over the casting list instead of the original dictionary does not return a Runtime error.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }  for i in list(cars):     cars["color"] = "black"      print(cars) 

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'} 

Appending Keys to an Empty List

To avoid changing the dictionary while iterating, we can create an empty list containing the dictionary keys while we perform the iteration. Using this empty list, we can append all the keys that we want to remove or change and then use the pop() function to remove the keys or the append function to add new key-value pairs.

This can be executed as shown in the code below.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }  list = []  for i in cars:     list.append(i)      for x in list:     if x == "model":         cars.pop(x)      print(cars) 

Output:

{'brand': 'Tesla', 'year': 2021} 

As shown below, we can add a new key-value pair to the dictionary using the same procedure while iterating using the for loop.

Sample Code:

cars = {  "brand": "Tesla",  "model": "Model S Plaid",  "year":  2021  }   list = []  for i in cars:     list.append(i)      for x in list:     cars["color"] = "black"    print(cars) 

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'} 

  • Related HOW TO?