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 19 Python
pytorch中tensor的合并与截取方法
Jul 26 Python
Python的iOS自动化打包实例代码
Nov 22 Python
Django如何开发简单的查询接口详解
May 17 Python
python在openstreetmap地图上绘制路线图的实现
Jul 11 Python
Django框架视图层URL映射与反向解析实例分析
Jul 29 Python
linux 下selenium chrome使用详解
Apr 02 Python
Python关键字及可变参数*args,**kw原理解析
Apr 04 Python
jupyter notebook插入本地图片的实现
Apr 13 Python
Python如何爬取51cto数据并存入MySQL
Aug 25 Python
matplotlib画混淆矩阵与正确率曲线的实例代码
Jun 01 Python
详解python的异常捕获
Mar 03 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连接和操作MySQL数据库基础教程
2014/09/29 PHP
php带抄送和密件抄送的邮件发送方法
2015/03/20 PHP
JavaScript 继承详解 第一篇
2009/08/30 Javascript
html组件不可输入(只读)同时任何组件都有效
2013/04/01 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
为JS扩展Array.prototype.indexOf引发的问题探讨及解决
2013/04/24 Javascript
javascript实现window.print()去除页眉页脚
2014/12/30 Javascript
nodejs加密Crypto的实例代码
2016/07/07 NodeJs
jQuery post数据至ashx实例详解
2016/11/18 Javascript
discuz表情的JS提取方法分析
2017/03/22 Javascript
vue2.0使用swiper组件实现轮播的示例代码
2018/03/03 Javascript
说说node中的可读流和可写流的区别
2018/06/01 Javascript
Electron autoUpdater实现Windows安装包自动更新的方法
2018/12/24 Javascript
JS实现商品橱窗特效
2020/01/09 Javascript
优化Vue中date format的性能详解
2020/01/13 Javascript
vue实现购物车列表
2020/06/30 Javascript
[02:23]DOTA2英雄基础教程 幻影长矛手
2013/12/09 DOTA
[01:45]绝对公平!DOTA2队长征召模式详解
2014/04/25 DOTA
Python 随机生成中文验证码的实例代码
2013/03/20 Python
python调用cmd复制文件代码分享
2013/12/27 Python
python实现批量改文件名称的方法
2015/05/25 Python
python 专题九 Mysql数据库编程基础知识
2017/03/16 Python
Pyorch之numpy与torch之间相互转换方式
2019/12/31 Python
python模式 工厂模式原理及实例详解
2020/02/11 Python
python实现手势识别的示例(入门)
2020/04/15 Python
python字典与json转换的方法总结
2020/12/28 Python
BudgetAir印度:预订航班、酒店和汽车租赁
2019/07/07 全球购物
PyQt QMainWindow的使用示例
2021/03/24 Python
三年级音乐教学反思
2014/01/28 职场文书
教师专业自荐信
2014/05/31 职场文书
感恩老师演讲稿400字
2014/08/28 职场文书
入党政审材料范文
2014/12/24 职场文书
小学生优秀评语
2014/12/29 职场文书
军训个人总结
2015/03/03 职场文书
读《推着妈妈去旅行》有感1500字
2019/10/15 职场文书
MongoDB误操作后使用oplog恢复数据
2022/04/11 MongoDB