#这里初始化一个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
{'a': 1, 'c': 1, 'd': 0}
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
#d.keys() 是一个下标的数组
>>> d.keys()
['a', 'c', 'b', 'd']
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
... if d[k] == 0:
... del(d[k])
...
>>> d
{'a': 1, 'c': 1}
#在Python2.x中结果也是对的,在Python3.x中会抛出一个dictionary changed size during iteration的异常
其实这个问题本来很简单,就是说如果遍历一个字典,但是在遍历中改变了他,比如增删某个元素,就会导致遍历退出,并且抛出一个dictionary changed size during iteration的异常
解决方法是遍历字典键值,以字典键值为依据遍历,这样改变了value以后不会影响遍历继续。
但是下面又有一位大神抛出高论:
首先,python 是推荐使用迭代器的,也就是 for k in adict 形式。其次,在遍历中删除容器中的元素,在 C++ STL 和 Python 等库中,都是不推荐的,因为这种情况往往说明了你的设计方案有问题,所以都有特殊要求。对应到 python 中,就是要使用 adict.keys() 做一个拷贝。最后,所有的 Python 容器都不承诺线程安全,你要多线程做这件事,本身就必须得加锁,这也说明了业务代码设计有问题的
但由“遍历中删除特定元素”这种特例,得出“遍历dict的时候,养成使用 for k in d.keys() 的习惯”,我觉得有必要纠正一下。在普通的遍历中,应该使用 for k in adict。
另外,对于“遍历中删除元素”这种需求,pythonic 的做法是 adict = {k, v for adict.items() if v != 0} 或 alist = [i for i in alist if i != 0]
Python代码
>>> alist = [1,2,0,3,0,4,5]
>>> alist = [i for i in alist if i != 0]
>>> alist
[1, 2, 3, 4, 5]
Python代码
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
>>> d = dict([(k,v) for k,v in d.iteritems() if v!=0])
>>> d
{'a':1,'c':1'}
如果大于Python>=2.7
还可以用这个写法:
Python代码
>>> d = {k:v for k,v in d.iteritems() if v !=0 }
遍历中删除元素确实是不好的设计习惯 应该说明令禁止的.
>>> 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
{'a': 1, 'c': 1, 'd': 0}
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
#d.keys() 是一个下标的数组
>>> d.keys()
['a', 'c', 'b', 'd']
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
... if d[k] == 0:
... del(d[k])
...
>>> d
{'a': 1, 'c': 1}
#在Python2.x中结果也是对的,在Python3.x中会抛出一个dictionary changed size during iteration的异常
其实这个问题本来很简单,就是说如果遍历一个字典,但是在遍历中改变了他,比如增删某个元素,就会导致遍历退出,并且抛出一个dictionary changed size during iteration的异常
解决方法是遍历字典键值,以字典键值为依据遍历,这样改变了value以后不会影响遍历继续。
但是下面又有一位大神抛出高论:
首先,python 是推荐使用迭代器的,也就是 for k in adict 形式。其次,在遍历中删除容器中的元素,在 C++ STL 和 Python 等库中,都是不推荐的,因为这种情况往往说明了你的设计方案有问题,所以都有特殊要求。对应到 python 中,就是要使用 adict.keys() 做一个拷贝。最后,所有的 Python 容器都不承诺线程安全,你要多线程做这件事,本身就必须得加锁,这也说明了业务代码设计有问题的
但由“遍历中删除特定元素”这种特例,得出“遍历dict的时候,养成使用 for k in d.keys() 的习惯”,我觉得有必要纠正一下。在普通的遍历中,应该使用 for k in adict。
另外,对于“遍历中删除元素”这种需求,pythonic 的做法是 adict = {k, v for adict.items() if v != 0} 或 alist = [i for i in alist if i != 0]
Python代码
>>> alist = [1,2,0,3,0,4,5]
>>> alist = [i for i in alist if i != 0]
>>> alist
[1, 2, 3, 4, 5]
Python代码
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
>>> d = dict([(k,v) for k,v in d.iteritems() if v!=0])
>>> d
{'a':1,'c':1'}
如果大于Python>=2.7
还可以用这个写法:
Python代码
>>> d = {k:v for k,v in d.iteritems() if v !=0 }
遍历中删除元素确实是不好的设计习惯 应该说明令禁止的.