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 cx_freeze打包工具处理问题思路及解决办法
Feb 13 Python
Windows安装Python、pip、easy_install的方法
Mar 05 Python
python绘制铅球的运行轨迹代码分享
Nov 14 Python
centos 安装python3.6环境并配置虚拟环境的详细教程
Feb 22 Python
Python利用matplotlib.pyplot绘图时如何设置坐标轴刻度
Apr 09 Python
使用Python自动化破解自定义字体混淆信息的方法实例
Feb 13 Python
Python列表(List)知识点总结
Feb 18 Python
python3 批量获取对应端口服务的实例
Jul 25 Python
解决django-xadmin列表页filter关联对象搜索问题
Nov 15 Python
Python彻底删除文件夹及其子文件方式
Dec 23 Python
pytorch-RNN进行回归曲线预测方式
Jan 14 Python
让你相见恨晚的十个Python骚操作
Nov 18 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中处理模拟rewrite 效果
2006/12/09 PHP
mysql中存储过程、函数的一些问题
2007/02/14 PHP
浅析PHP的ASCII码转换类
2013/07/05 PHP
php使用curl模拟登录后采集页面的例子
2013/11/04 PHP
JavaScript的面向对象方法以及差别
2008/03/31 Javascript
javascript中的关于类型转换的性能优化
2010/12/14 Javascript
仿jQuery的siblings效果的js代码
2011/08/09 Javascript
给事件响应函数传参数的四种方式小结
2013/12/05 Javascript
JS创建类和对象的两种不同方式
2014/08/08 Javascript
Knockout自定义绑定创建方法
2015/12/26 Javascript
JS控件bootstrap suggest plugin使用方法详解
2017/03/25 Javascript
JS条形码(一维码)插件JsBarcode用法详解【编码类型、参数、属性】
2017/04/19 Javascript
JS实现不用中间变量temp 实现两个变量值得交换方法
2018/02/04 Javascript
Vue.directive 自定义指令的问题小结
2018/03/04 Javascript
关于HTML5的data-*自定义属性的总结
2018/05/05 Javascript
微信小程序踩坑记录之解决tabBar.list[3].selectedIconPath大小超过40kb
2018/07/04 Javascript
详解JavaScript事件循环机制
2018/09/07 Javascript
[43:53]OG vs EG 2019国际邀请赛淘汰赛 胜者组 BO3 第三场 8.22
2019/09/05 DOTA
Python将图片批量从png格式转换至WebP格式
2020/08/22 Python
Python内存读写操作示例
2018/07/18 Python
十行代码使用Python写一个USB病毒
2019/06/21 Python
python之拟合的实现
2019/07/19 Python
Python openpyxl 插入折线图实例
2020/04/17 Python
详解java调用python的几种用法(看这篇就够了)
2020/12/10 Python
大学生新闻专业个人自我评价
2013/11/12 职场文书
区域总监的岗位职责
2013/11/21 职场文书
策划主管的工作职责
2013/11/24 职场文书
总经理秘书岗位职责
2014/03/17 职场文书
导师推荐信范文
2014/05/09 职场文书
投标承诺书怎么写
2014/05/24 职场文书
人事主管岗位职责说明书
2014/07/30 职场文书
公务员培的训心得体会
2014/09/01 职场文书
公司领导班子民主生活会对照检查材料
2014/10/02 职场文书
小学工作总结2015
2015/05/04 职场文书
因公司原因离职的辞职信范文
2015/05/12 职场文书
民事答辩状格式范文
2015/05/21 职场文书