简体   繁体   中英

How to avoid "RuntimeError: dictionary changed size during iteration" error?

I have a dictionary of lists in which some of the values are empty:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

At the end of creating these lists, I want to remove these empty lists before returning my dictionary. I tried doing it like this:

for i in d:
    if not d[i]:
        d.pop(i)

but I got a RuntimeError . I am aware that you cannot add/remove elements in a dictionary while iterating through it...what would be a way around this then?


See Modifying a Python dict while iterating over it for citations that this can cause problems, and why.

In Python 3.x and 2.x you can use use list to force a copy of the keys to be made:

for i in list(d):

In Python 2.x callingkeys made a copy of the keys that you could iterate over while modifying the dict :

for i in d.keys():

But note that in Python 3.x this second method doesn't help with your error because keys returns an a view object instead of copynig the keys into a list.

You only need to use copy :

This way you iterate over the original dictionary fields and on the fly can change the desired dict d . It works on each Python version, so it's more clear.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

(BTW - Generally to iterate over copy of your data structure, instead of using .copy for dictionaries or slicing [:] for lists, you can use import copy -> copy.copy (for shallow copy which is equivalent to copy that is supported by dictionaries or slicing [:] that is supported by lists) or copy.deepcopy on your data structure.)

Just use dictionary comprehension to copy the relevant items into a new dict:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

For this in Python 2:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

This worked for me:

d = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(d.items()):
    if value == '':
        del d[key]
print(d)
# {1: 'a', 3: 'b', 6: 'c'}

Casting the dictionary items to list creates a list of its items, so you can iterate over it and avoid the RuntimeError .

I would try to avoid inserting empty lists in the first place, but, would generally use:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

If prior to 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

or just:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

对于 Python 3:

{k:v for k,v in d.items() if v}

to avoid "dictionary changed size during iteration error".

for example : "when you try to delete some key" ,

just use 'list' with '.items()' , and here is a simple example :

my_dict = {
    'k1':1,
    'k2':2,
    'k3':3,
    'k4':4
 
    }
    
print(my_dict)

for key, val in list(my_dict.items()):
    if val == 2 or val == 4:
        my_dict.pop(key)

print(my_dict)

+++ output :

{'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4}

{'k1': 1, 'k3': 3}

+++

this is just example and change it based on your case/requirements, i hope this helpful.

You cannot iterate through a dictionary while its changing during for loop. Make a casting to list and iterate over that list, it works for me.

    for key in list(d):
        if not d[key]: 
            d.pop(key)

Python 3 does not allow deletion while iterating (using for loop above) dictionary. There are various alternatives to do; one simple way is the to change following line

for i in x.keys():

With

for i in list(x)

The reason for the runtime error is that you cannot iterate through a data structure while its structure is changing during iteration.

One way to achieve what you are looking for is to use list to append the keys you want to remove and then use pop function on dictionary to remove the identified key while iterating through the list.

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)
dictc={"stName":"asas"}
keys=dictc.keys()
for key in list(keys):
    dictc[key.upper()] ='New value'
print(str(dictc))

For situations like this, i like to make a deep copy and loop through that copy while modifying the original dict.

If the lookup field is within a list, you can enumerate in the for loop of the list and then specify the position as index to access the field in the original dict.

If the values in the dictionary are unique too, then I used this solution

        keyToBeDeleted=None
        for k,v in mydict.items():
            if(v==match):
                keyToBeDeleted=k
                break
        mydict.pop(keyToBeDeleted,None)
  • The Python "RuntimeError: dictionary changed size during iteration" occurs when we change the size of a dictionary when iterating over it.

  • To solve the error, use the copy() method to create a shallow copy of the dictionary that you can iterate over, eg my_dict.copy() .

my_dict = {'a': 1, 'b': 2, 'c': 3}

for key in my_dict.copy():
    print(key)
    if key == 'b':
        del my_dict[key]

print(my_dict) # 👉️ {'a': 1, 'c': 3}
  • You can also convert the keys of the dictionary to a list and iterate over the list of keys.
my_dict = {'a': 1, 'b': 2, 'c': 3}

for key in list(my_dict.keys()):
    print(key)
    if key == 'b':
        del my_dict[key]

print(my_dict)  # 👉️ {'a': 1, 'c': 3}

Nested null values

Let's say we have a dictionary with nested keys, some of which are null values:

dicti = {
"k0_l0":{
    "k0_l1": {
        "k0_l2": {
                "k0_0":None,
                "k1_1":1,
                "k2_2":2.2
                }
        },
        "k1_l1":None,
        "k2_l1":"not none",
        "k3_l1":[]
    },
    "k1_l0":"l0"
}

Then we can remove the null values using this function:

def pop_nested_nulls(dicti):
    for k in list(dicti):
        if isinstance(dicti[k], dict):
            dicti[k] = pop_nested_nulls(dicti[k])
        elif not dicti[k]:
            dicti.pop(k)
    return dicti

Output for pop_nested_nulls(dicti)

{'k0_l0': {'k0_l1': {'k0_l2': {'k1_1': 1,
                               'k2_2': 2.2}},
           'k2_l1': 'not '
                    'none'},
 'k1_l0': 'l0'}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM