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写的Socks5协议代理服务器
Aug 06 Python
Python中使用HTMLParser解析html实例
Feb 08 Python
浅谈Python实现Apriori算法介绍
Dec 20 Python
python实现停车管理系统
Nov 30 Python
浅谈Python 列表字典赋值的陷阱
Jan 20 Python
python开发之anaconda以及win7下安装gensim的方法
Jul 05 Python
Python Pandas对缺失值的处理方法
Sep 27 Python
Pytorch中index_select() 函数的实现理解
Nov 19 Python
关于Python解包知识点总结
May 05 Python
实例代码讲解Python 线程池
Aug 24 Python
python使用shell脚本创建kafka连接器
Apr 29 Python
Python时间操作之pytz模块使用详解
Jun 14 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
索尼SONY ICF-SW7600GR电路分析与改良
2021/03/02 无线电
php中通过curl检测页面是否被百度收录
2013/09/27 PHP
解决file_get_contents无法请求https连接的方法
2013/12/17 PHP
PHP文件缓存内容保存格式实例分析
2014/08/20 PHP
php购物车实现方法
2015/01/03 PHP
简单谈谈favicon
2015/06/10 PHP
php实现比较两个文件夹异同的方法
2015/06/18 PHP
PHP实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
layui数据表格自定义每页条数limit设置
2019/10/26 PHP
Javascript实现的分页函数
2006/12/22 Javascript
javascript getElementsByTagName
2011/01/31 Javascript
javascript suggest效果 自动完成实现代码分享
2012/02/17 Javascript
javascript学习笔记(七) js函数介绍
2012/06/19 Javascript
jQuery获取Select选择的Text和Value(详细汇总)
2013/01/25 Javascript
html+css+js实现xp window界面及有关功能
2013/03/26 Javascript
AngularJS入门教程之ng-class 指令用法
2016/08/01 Javascript
JS+CSS3制作炫酷的弹窗效果
2016/11/08 Javascript
JS编写函数实现对身份证号码最后一位的验证功能
2016/12/29 Javascript
jQuery实现CheckBox全选、全不选功能
2017/01/11 Javascript
使用vue-router与v-if实现tab切换遇到的问题及解决方法
2018/09/07 Javascript
python实现上传样本到virustotal并查询扫描信息的方法
2014/10/05 Python
Python中实现从目录中过滤出指定文件类型的文件
2015/02/02 Python
python 与GO中操作slice,list的方式实例代码
2017/03/20 Python
详解使用 pyenv 管理多个版本 python 环境
2017/10/19 Python
PYQT5实现控制台显示功能的方法
2019/06/25 Python
python3.6 tkinter实现屏保小程序
2019/07/30 Python
美国正版电视节目和电影在线观看:Hulu
2018/05/24 全球购物
阿联酋最好的手机、电子产品和家用电器网上商店:Eros Digital Home
2020/08/09 全球购物
TCP/IP模型的分界线
2012/12/01 面试题
中专生毕业自我鉴定
2013/11/01 职场文书
企业总经理任命书
2014/06/05 职场文书
小学生勤俭节约演讲稿
2014/08/28 职场文书
学雷锋活动倡议书
2014/08/30 职场文书
县政协领导班子群众路线教育实践活动四风问题整改方案
2014/10/26 职场文书
给老婆道歉的话
2015/01/20 职场文书
JavaScript如何优化逻辑判断代码详解
2021/06/08 Javascript