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 相关文章推荐
go语言计算两个时间的时间差方法
Mar 13 Python
Python中有趣在__call__函数
Jun 21 Python
举例讲解Python设计模式编程中的访问者与观察者模式
Jan 26 Python
Django+Ajax+jQuery实现网页动态更新的实例
May 28 Python
python计算列表内各元素的个数实例
Jun 29 Python
解决python3 pika之连接断开的问题
Dec 18 Python
在OpenCV里使用Camshift算法的实现
Nov 22 Python
pyenv虚拟环境管理python多版本和软件库的方法
Dec 26 Python
Python 面向对象之类class和对象基本用法示例
Feb 02 Python
在python3.64中安装pyinstaller库的方法步骤
Jun 02 Python
python生成随机数、随机字符、随机字符串
Apr 06 Python
Python数组变形的几种实现方法
May 30 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版本号
2006/10/09 PHP
网页游戏开发入门教程二(游戏模式+系统)
2009/11/02 PHP
PHP iconv 解决utf-8和gb2312编码转换问题
2010/04/12 PHP
CI分页类首页、尾页不显示的解决方法
2016/03/28 PHP
浅谈PHP之ThinkPHP框架使用详解
2020/07/21 PHP
js checkbox(复选框) 使用集锦
2009/04/28 Javascript
javascript中的prototype属性实例分析说明
2010/08/09 Javascript
jquery实现邮箱自动补全功能示例分享
2014/02/17 Javascript
jQuery中:reset选择器用法实例
2015/01/04 Javascript
Angularjs实现多个页面共享数据的方式
2016/03/29 Javascript
JS检测移动端横竖屏的代码
2016/05/30 Javascript
jQuery插件HighCharts绘制2D饼图效果示例【附demo源码下载】
2017/03/21 jQuery
Vue实现动态创建和删除数据的方法
2018/03/17 Javascript
angular2 ng2-file-upload上传示例代码
2018/08/23 Javascript
js实现简易点击切换显示或隐藏
2020/11/29 Javascript
python中的reduce内建函数使用方法指南
2014/08/31 Python
跟老齐学Python之有容乃大的list(3)
2014/09/15 Python
python结合API实现即时天气信息
2016/01/19 Python
Python中的is和==比较两个对象的两种方法
2017/09/06 Python
python在非root权限下的安装方法
2018/01/23 Python
python调用API实现智能回复机器人
2018/04/10 Python
python如何查看微信消息撤回
2018/11/27 Python
Python判断三段线能否构成三角形的代码
2020/04/12 Python
Parfumdreams英国:香水和化妆品
2019/05/10 全球购物
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?用contains来区分是否有重复的对象。还是都不用
2013/07/30 面试题
成考报名单位证明范本
2014/01/16 职场文书
十佳大学生事迹材料
2014/01/29 职场文书
人事主管岗位职责
2014/01/30 职场文书
暂停营业通知
2015/04/25 职场文书
严以律己学习心得体会
2016/01/13 职场文书
民警忠诚教育心得体会
2016/01/23 职场文书
PHP使用QR Code生成二维码实例
2021/07/07 PHP
K8s部署发布Golang应用程序的实现方法
2021/07/16 Golang
Python识别花卉种类鉴定网络热门植物并自动整理分类
2022/04/08 Python
Nginx本地配置SSL访问的实例教程
2022/05/30 Servers
Mysql中@和@@符号的详细使用指南
2022/06/05 MySQL