Python中遍历字典过程中更改元素导致异常的解决方法


Posted in Python onMay 12, 2016

先来回顾一下Python中遍历字典的一些基本方法:
脚本:

#!/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

 

执行结果:

##########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

嗯,然后我们进入“正题”--

一段关于Python字典遍历的“争论”....
先摘抄下:

#这里初始化一个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}
#结果也是对的
>>>

#这里初始化一个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}
#结果也是对的
>>>

其实这个问题本来很简单,就是说如果遍历一个字典,但是在遍历中改变了他,比如增删某个元素,就会导致遍历退出,并且抛出一个dictionary changed size during iteration的异常.
解决方法是遍历字典键值,以字典键值为依据遍历,这样改变了value以后不会影响遍历继续。
但是下面又有一位大神抛出高论:

首先,python 是推荐使用迭代器的,也就是 for k in adict 形式。其次,在遍历中删除容器中的元素,在 C++ STL 和 Python 等库中,都是不推荐的,因为这种情况往往说明了你的设计方案有问题,所有都有特殊要求,对应到 python 中,就是要使用 adict.key() 做一个拷贝。最后,所有的 Python 容器都不承诺线程安全,你要多线程做这件事,本身就必须得加锁,这也说明了业务代码设计有问题的.

但由“遍历中删除特定元素”这种特例,得出“遍历dict的时候,养成使用 for k in d.keys() 的习惯”,我觉得有必要纠正一下。在普通的遍历中,应该使用 for k in adict。
另外,对于“遍历中删除元素”这种需求,pythonic 的做法是 adict = {k, v for adict.iteritems() if v != 0} 或 alist = [i for i in alist if i != 0]

这个写法让我眼前一亮:怎么还有这个语法?
再仔细一看,他可能是这个意思:

#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = {'a':1, 'b':0, 'c':1, 'd':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 = {'a':1, 'b':0, 'c':1, 'd':0}
b={}
for k,v in a.items():
  if v != 0:
    b.update({k:v})
adict = b
del b
print a

不知道对不对。
因为这个写法一开始让我猛然想到三元操作符,仔细一看才发现不是,以前Goolge到有个解决方案

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是个逻辑表达式,返回0或者1,刚好作为前面那个元组的ID来取值,实在是太妙了。。。
不过在Google的资料里面还有一个版本

#V1 if X else V2
s = None
a = "not null" if s == None else s
print a
#'not null'

后来发帖在华蟒用户组(中文Python技术邮件列表)中提到后众多大神解答如下:

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

[1, 2, 3, 4, 5]

>>> 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
还可以用这个写法:

>>> d = {k:v for k,v in d.iteritems() if v !=0 }
Python 相关文章推荐
python利用拉链法实现字典方法示例
Mar 25 Python
浅谈python和C语言混编的几种方式(推荐)
Sep 27 Python
python编程使用协程并发的优缺点
Sep 20 Python
python装饰器常见使用方法分析
Jun 26 Python
python笔记_将循环内容在一行输出的方法
Aug 08 Python
python 基于dlib库的人脸检测的实现
Nov 08 Python
原来我一直安装 Python 库的姿势都不对呀
Nov 11 Python
python使用pip安装SciPy、SymPy、matplotlib教程
Nov 20 Python
Python PyQt5模块实现窗口GUI界面代码实例
May 12 Python
python怎么判断素数
Jul 01 Python
python的数学算法函数及公式用法
Nov 18 Python
python 基于opencv去除图片阴影
Jan 26 Python
Python安装使用命令行交互模块pexpect的基础教程
May 12 #Python
Python下载指定页面上图片的方法
May 12 #Python
Python基于二分查找实现求整数平方根的方法
May 12 #Python
python二分查找算法的递归实现方法
May 12 #Python
Python数据类型详解(四)字典:dict
May 12 #Python
Python匹配中文的正则表达式
May 11 #Python
Python3使用requests发闪存的方法
May 11 #Python
You might like
php实现设计模式中的单例模式详解
2014/10/11 PHP
PHP获取服务器端信息的方法
2014/11/28 PHP
php银联网页支付实现方法
2015/03/04 PHP
基于Swoole实现PHP与websocket聊天室
2016/08/03 PHP
Yii2.0 RESTful API 基础配置教程详解
2018/12/26 PHP
Smarty模板变量与调节器实例详解
2019/07/20 PHP
laravel 出现command not found问题的解决方案
2019/10/23 PHP
可实现多表单提交的javascript函数
2007/08/01 Javascript
JavaScript调用后台的三种方法实例
2013/10/17 Javascript
基于编写jQuery的无缝滚动插件
2014/08/02 Javascript
AngularJS基础知识笔记之过滤器
2015/05/10 Javascript
使用CDN和AJAX加速WordPress中jQuery的加载
2015/12/05 Javascript
jQuery过滤特殊字符及JS字符串转为数字
2016/05/26 Javascript
详解打造 Vue.js 可复用组件
2017/03/24 Javascript
jQuery实现form表单序列化转换为json对象功能示例
2018/05/23 jQuery
jquery层次选择器的介绍
2019/01/18 jQuery
详解vue中this.$emit()的返回值是什么
2019/04/07 Javascript
bootstrap tooltips在 angularJS中的使用方法
2019/04/10 Javascript
js实现图片无缝循环轮播
2019/10/28 Javascript
python线程、进程和协程详解
2016/07/19 Python
Pandas中把dataframe转成array的方法
2018/04/13 Python
详解python运行三种方式
2019/05/13 Python
Python+Django+MySQL实现基于Web版的增删改查的示例代码
2020/05/13 Python
css3实现二维码扫描特效的示例
2020/10/29 HTML / CSS
利用html5 canvas破解简单验证码及getImageData接口应用
2013/01/25 HTML / CSS
html5拖拽应用记录及注意点
2020/05/27 HTML / CSS
英国女性时尚精品店:THE DRESSING ROOM
2018/05/23 全球购物
五星级酒店餐饮部总监的标准岗位职责
2014/02/17 职场文书
应聘编辑自荐信范文
2014/03/12 职场文书
户籍证明格式
2014/09/15 职场文书
幼儿园万圣节活动总结
2015/05/05 职场文书
大学优秀学生主要事迹材料
2015/11/04 职场文书
2016年学生会感恩节活动总结
2016/04/01 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
Mysql基础之常见函数
2021/04/22 MySQL
如何用Python搭建gRPC服务
2021/06/30 Python