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中正则表达式的使用详解
Oct 17 Python
C#返回当前系统所有可用驱动器符号的方法
Apr 18 Python
Python Django使用forms来实现评论功能
Aug 17 Python
python实现读取并显示图片的两种方法
Jan 13 Python
Django中使用Celery的教程详解
Aug 24 Python
Django ManyToManyField 跨越中间表查询的方法
Dec 18 Python
Python 取numpy数组的某几行某几列方法
Oct 24 Python
python错误调试及单元文档测试过程解析
Dec 19 Python
Python GUI编程学习笔记之tkinter事件绑定操作详解
Mar 30 Python
Python使用lambda抛出异常实现方法解析
Aug 20 Python
python使用selenium爬虫知乎的方法示例
Oct 28 Python
python语言实现贪吃蛇游戏
Nov 13 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生成Flash动画的实现代码
2010/03/12 PHP
apache配置虚拟主机的方法详解
2013/06/17 PHP
Yii框架中 find findAll 查找出制定的字段的方法对比
2014/09/10 PHP
删除PHP数组中头部、尾部、任意元素的实现代码
2017/04/10 PHP
PHP开发中解决并发问题的几种实现方法分析
2017/11/13 PHP
Laravel使用消息队列需要注意的一些问题
2017/12/13 PHP
Javascript 事件流和事件绑定
2009/07/16 Javascript
表单类各种类型(文本框)失去焦点效果jquery代码
2013/04/26 Javascript
Jquery动态更改一张位图的src与Attr的使用
2013/07/31 Javascript
js使用eval解析json实例与注意事项分享
2014/01/18 Javascript
js数组去重的常用方法总结
2014/01/24 Javascript
javascript实现的元素拖动函数宿主为浏览器
2014/07/21 Javascript
JavaScript组合拼接字符串的效率对比测试
2014/11/06 Javascript
浅谈document.write()输出样式
2015/05/07 Javascript
IE10中flexigrid无法显示数据的解决方法
2015/07/26 Javascript
jQuery Mobile 和 Kendo UI 的比较
2016/05/05 Javascript
jQuery插件EasyUI获取当前Tab中iframe窗体对象的方法
2016/08/05 Javascript
单页面vue引入百度统计的使用方法示例详解
2018/10/13 Javascript
详解vite2.0配置学习(typescript版本)
2021/02/25 Javascript
浅谈python实现Google翻译PDF,解决换行的问题
2018/11/28 Python
详解python的argpare和click模块小结
2019/03/31 Python
python爬虫豆瓣网的模拟登录实现
2019/08/21 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
2020/01/09 Python
Python基于Socket实现简单聊天室
2020/02/17 Python
Python+PyQt5+MySQL实现天气管理系统
2020/06/16 Python
CSS3系列教程:背景图片(背景大小和多背景图) 应用说明
2012/12/19 HTML / CSS
详解CSS3中字体平滑处理和抗锯齿渲染
2017/03/29 HTML / CSS
结构工程研究生求职信
2013/10/13 职场文书
个人能力自我鉴赏
2014/01/25 职场文书
租房协议书范本
2014/04/09 职场文书
文明工地标语
2014/06/16 职场文书
内勤岗位职责
2015/02/10 职场文书
小学德育工作总结2015
2015/05/12 职场文书
Redis遍历所有key的两个命令(KEYS 和 SCAN)
2021/04/12 Redis
详解MySQL的Seconds_Behind_Master
2021/05/18 MySQL
入门学习Go的基本语法
2021/07/07 Golang