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使用PyGame绘制图像并保存为图片文件的方法
Apr 24 Python
5种Python单例模式的实现方式
Jan 14 Python
Python3控制路由器——使用requests重启极路由.py
May 11 Python
python用Pygal如何生成漂亮的SVG图像详解
Feb 10 Python
解决python3 urllib中urlopen报错的问题
Mar 25 Python
Python利用Beautiful Soup模块搜索内容详解
Mar 29 Python
go和python变量赋值遇到的一个问题
Aug 31 Python
Python基于回溯法子集树模板解决选排问题示例
Sep 07 Python
python3 自动识别usb连接状态,即对usb重连的判断方法
Jul 03 Python
浅谈Python 敏感词过滤的实现
Aug 15 Python
python高阶函数map()和reduce()实例解析
Mar 16 Python
升级keras解决load_weights()中的未定义skip_mismatch关键字问题
Jun 12 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
世界收音机发展史
2021/03/01 无线电
高亮度显示php源代码
2006/10/09 PHP
phpmyadmin出现Cannot start session without errors问题解决方法
2014/08/14 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
详解PHP原生DOM对象操作XML的方法
2016/10/17 PHP
读jQuery之三(构建选择器)
2011/06/11 Javascript
js仿百度贴吧验证码特效实例代码
2014/01/16 Javascript
jQuery给多个不同元素添加class样式的方法
2015/03/26 Javascript
JavaScript实现简单图片翻转的方法
2015/04/17 Javascript
jQuery常用且重要方法汇总
2015/07/13 Javascript
浅谈jquery的html方法里包含特殊字符的处理
2016/11/30 Javascript
jQuery实现的简单拖动层示例
2017/02/22 Javascript
浅谈react.js 之 批量添加与删除功能
2017/04/17 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
2017/06/13 Javascript
Vue侧滑菜单组件——DrawerLayout
2017/12/18 Javascript
NodeJS父进程与子进程资源共享原理与实现方法
2018/03/16 NodeJs
让webpack+vue-cil项目不再自动打开浏览器的方法
2018/09/27 Javascript
jQuery实现的自定义轮播图功能详解
2018/12/28 jQuery
利用scrapy将爬到的数据保存到mysql(防止重复)
2018/03/31 Python
Python Selenium Cookie 绕过验证码实现登录示例代码
2018/04/10 Python
超简单的Python HTTP服务
2019/07/22 Python
Python+appium框架原生代码实现App自动化测试详解
2020/03/06 Python
python计算Content-MD5并获取文件的Content-MD5值方式
2020/04/03 Python
浅谈Python xlwings 读取Excel文件的正确姿势
2021/02/26 Python
IE支持HTML5的解决方法
2009/10/20 HTML / CSS
html5定位获取当前位置并在百度地图上显示
2014/08/22 HTML / CSS
澳大利亚工具仓库:Tools Warehouse
2018/10/15 全球购物
Armor Lux法国官方网站:水手服装、成衣和内衣
2020/05/26 全球购物
使用索引(Index)有哪些需要考虑的因素
2016/10/19 面试题
艺术系应届生的自我评价
2013/10/19 职场文书
自我评价范文
2013/12/22 职场文书
承认错误的检讨书
2014/01/30 职场文书
大学军训感言600字
2014/02/25 职场文书
《海伦?凯勒》教学反思
2014/04/17 职场文书
2016年庆“七一”主题党日活动总结
2016/04/05 职场文书
基于Go Int转string几种方式性能测试
2021/04/28 Golang