Home  >  Article  >  Backend Development  >  Changing elements during dictionary traversal in Python causes exceptions

Changing elements during dictionary traversal in Python causes exceptions

高洛峰
高洛峰Original
2017-03-02 16:49:241067browse

Let’s first review some basic methods of traversing dictionaries in Python:

Script:

#!/usr/bin/python 
dict={"a":"apple","b":"banana","o":"orange"} 
 
print "##########dict######################" 
for i in dict: 
    print "dict[%s]=" % i,dict[i] 
 
print "###########items#####################" 
for (k,v) in dict.items(): 
    print "dict[%s]=" % k,v 
 
print "###########iteritems#################" 
for k,v in dict.iteritems(): 
    print "dict[%s]=" % k,v 
 
print "###########iterkeys,itervalues#######" 
for k,v in zip(dict.iterkeys(),dict.itervalues()): 
    print "dict[%s]=" % k,v

Execution results:

##########dict###################### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########items##################### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########iteritems################# 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########iterkeys,itervalues####### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange

Well, then we enter the "main topic"--

A paragraph about Python dictionary traversal" Debate"....
Excerpt first:

#这里初始化一个dict
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
#本意是遍历dict,发现元素的值是0的话,就删掉
>>> for k in d:
...  if d[k] == 0:
...   del(d[k])
...
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
#结果抛出异常了,两个0的元素,也只删掉一个。
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1, &#39;d&#39;: 0}

>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#d.keys() 是一个下标的数组
>>> d.keys()
[&#39;a&#39;, &#39;c&#39;, &#39;b&#39;, &#39;d&#39;]
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
...  if d[k] == 0:
...   del(d[k])
...
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1}
#结果也是对的
>>>

#这里初始化一个dict
>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#本意是遍历dict,发现元素的值是0的话,就删掉
>>> for k in d:
...  if d[k] == 0:
...   del(d[k])
...
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
#结果抛出异常了,两个0的元素,也只删掉一个。
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1, &#39;d&#39;: 0}
 
>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#d.keys() 是一个下标的数组
>>> d.keys()
[&#39;a&#39;, &#39;c&#39;, &#39;b&#39;, &#39;d&#39;]
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
...  if d[k] == 0:
...   del(d[k])
...
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1}
#结果也是对的
>>>

In fact, this problem is very simple, that is, if you traverse a dictionary, but If it is changed during the traversal, such as adding or deleting an element, the traversal will exit and an exception of dictionary changed size during iteration will be thrown.
The solution is to traverse the dictionary key value and traverse based on the dictionary key value. Changing the value in this way will not affect the continuation of the traversal.
But here is another great expert who gives a high opinion:

First of all, python recommends the use of iterators, that is, for k in adict form. Secondly, deleting elements in the container during traversal is not recommended in libraries such as C++ STL and Python, because this situation often indicates that there is a problem with your design plan, and all have special requirements, which correspond to python. , just use adict.key() to make a copy. Finally, all Python containers do not promise thread safety. If you want to do this with multiple threads, you must lock it. This also shows that there is a problem with the business code design.

But by "Traversing In the special case of "delete specific elements", I came to the conclusion that "when traversing dict, develop the habit of using for k in d.keys()", I think it is necessary to correct it. In ordinary traversal, you should use for k in adict.
In addition, for the requirement of "removing elements during traversal", the pythonic approach is adict = {k, v for adict.iteritems() if v != 0} or alist = [i for i in alist if i ! = 0]

This way of writing made my eyes light up: Why is there still this syntax?
Looking carefully, he may mean this:

#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
b={}
for k,v in a.items():
  if v != 0:
    b.update({k:v})
adict = b
del b
print a

#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
b={}
for k,v in a.items():
  if v != 0:
    b.update({k:v})
adict = b
del b
print a

I don’t know if it’s right.
Because this way of writing suddenly made me think of the ternary operator at first. After a closer look, I realized that it was not the case. There was a solution in Google before

val = float(raw_input("Age: "))
status = ("working","retired")[val>65]
print "You should be",status

val = float(raw_input("Age: "))
status = ("working","retired")[val>65]
print "You should be",status

val>65 is a logical expression, returning 0 or 1, which happens to be the ID of the previous tuple, which is really wonderful. . .
But there is also a version in Google's information

#V1 if X else V2
s = None
a = "not null" if s == None else s
print a
#&#39;not null&#39;

Later, I posted a post and mentioned it in the Huaman user group (Chinese Python technical mailing list) Many experts have answered as follows:

>>> alist = [1,2,0,3,0,4,5]
>>> alist = [i for i in alist if i != 0]
>>> alist

[1, 2, 3, 4, 5]

>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
>>> d = dict([(k,v) for k,v in d.iteritems() if v!=0])
>>> d
{&#39;a&#39;:1,&#39;c&#39;:1&#39;}

If it is larger than Python>=2.7
, you can also use this writing method:

>>> d = {k:v for k,v in d.iteritems() if v !=0 }


For more related articles about exceptions caused by changing elements during dictionary traversal in Python, please pay attention to the PHP Chinese website!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn