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使用 HTMLTestRunner.py生成测试报告
Oct 20 Python
Python+Turtle动态绘制一棵树实例分享
Jan 16 Python
Python判断以什么结尾以什么开头的实例
Oct 27 Python
python脚本执行CMD命令并返回结果的例子
Aug 14 Python
pytorch梯度剪裁方式
Feb 04 Python
Python动态强类型解释型语言原理解析
Mar 25 Python
如何安装并在pycharm使用selenium的方法
Apr 30 Python
TensorFlow固化模型的实现操作
May 26 Python
python向企业微信发送文字和图片消息的示例
Sep 28 Python
Python+kivy BoxLayout布局示例代码详解
Dec 28 Python
python包的导入方式总结
Mar 02 Python
PyTorch dropout设置训练和测试模式的实现
May 27 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
《星际争霸II》全新指挥官斯台特曼现已上线
2020/03/08 星际争霸
双料怀旧--SHARP GF515的维护、修理和简单调试
2021/03/02 无线电
PHP5.0正式发布 不完全兼容PHP4 新增多项功能
2006/10/09 PHP
PHP4实际应用经验篇(3)
2006/10/09 PHP
php ajax异步读取rss文档数据
2016/03/29 PHP
ThinkPHP中create()方法自动验证实例
2017/04/26 PHP
PHP+MySQL实现模糊查询员工信息功能示例
2018/06/01 PHP
javascript来定义类的规范小结
2010/11/19 Javascript
html+javascript实现可拖动可提交的弹出层对话框效果
2013/08/05 Javascript
javascript实现的平方米、亩、公顷单位换算小程序
2014/08/11 Javascript
JS实现alert中显示换行的方法
2015/12/17 Javascript
Vue实现双向绑定的方法
2016/12/22 Javascript
JS实现touch 点击滑动轮播实例代码
2017/01/19 Javascript
Vue2.0组件间数据传递示例
2017/03/07 Javascript
js canvas实现星空连线背景特效
2019/11/01 Javascript
JS实现百度搜索框关键字推荐
2020/02/17 Javascript
vue3.0 上手体验
2020/09/21 Javascript
如何在面试中手写出javascript节流和防抖函数
2020/10/22 Javascript
[44:40]Spirit vs Navi Supermajor小组赛 A组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
python Crypto模块的安装与使用方法
2017/12/21 Python
利用Pyhton中的requests包进行网页访问测试的方法
2018/12/26 Python
python实现转盘效果 python实现轮盘抽奖游戏
2019/01/22 Python
解决tensorflow由于未初始化变量而导致的错误问题
2020/01/06 Python
Python实现汇率转换操作
2020/05/03 Python
茵宝(Umbro)英国官方商店:英国足球服装生产商
2016/12/29 全球购物
香港连卡佛百货官网:Lane Crawford
2019/09/04 全球购物
WebSphere面试题:在WebSphere里面如何部署一个应用
2015/08/02 面试题
生日邀请函范文
2014/01/13 职场文书
员工工作表现评语
2014/04/26 职场文书
个人欠款担保书
2014/05/20 职场文书
2014年办公室工作总结范文
2014/11/12 职场文书
2015年度村委会工作总结
2015/04/29 职场文书
奔腾年代观后感
2015/06/09 职场文书
青年教师听课心得体会
2016/01/15 职场文书
Window server中安装Redis的超详细教程
2021/11/17 Redis
苹果的回收机器人可以通过拆解iPhone获取大量的金和铜并外公布了环境保护最新进展
2022/04/21 数码科技