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实现运行其他程序的四种方式实例分析
Aug 17 Python
Python 读取图片文件为矩阵和保存矩阵为图片的方法
Apr 27 Python
python中sys.argv函数精简概括
Jul 08 Python
Sanic框架蓝图用法实例分析
Jul 17 Python
把pandas转换int型为str型的方法
Jan 29 Python
Python图像处理之图片文字识别功能(OCR)
Jul 30 Python
Python调用钉钉自定义机器人的实现
Jan 03 Python
python matplotlib中的subplot函数使用详解
Jan 19 Python
python GUI库图形界面开发之PyQt5浏览器控件QWebEngineView详细使用方法
Feb 26 Python
Python如何实现自带HTTP文件传输服务
Jul 08 Python
Python特殊属性property原理及使用方法解析
Oct 09 Python
Pytorch可视化的几种实现方法
Jun 10 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 出现乱码和Sessions验证问题的解决方法!
2008/12/06 PHP
表格展示无限级分类(PHP版)
2012/08/21 PHP
php strrpos()与strripos()函数
2013/08/31 PHP
YII2自动登录Cookie总是失效的解决方法
2017/06/28 PHP
PHP实现网站应用微信登录功能详解
2019/04/11 PHP
JavaScipt基本教程之前言
2008/01/16 Javascript
基于jquery的checkbox下拉框插件代码
2010/06/25 Javascript
jQuery前台数据获取实现代码
2011/03/16 Javascript
JQuery里面的几种选择器 查找满足条件的元素$(&quot;#控件ID&quot;)
2011/08/23 Javascript
一个奇葩的最短的 IE 版本判断JS脚本
2014/05/28 Javascript
JS实现窗口加载时模拟鼠标移动的方法
2015/06/03 Javascript
13个PHP函数超实用
2015/10/21 Javascript
基于bootstrap实现广告轮播带图片和文字效果
2016/07/22 Javascript
Google 地图类型详解及示例代码
2016/08/06 Javascript
js图片轮播手动切换特效
2017/01/12 Javascript
VUE-Table上绑定Input通过render实现双向绑定数据的示例
2018/08/27 Javascript
vue webpack打包后图片路径错误的完美解决方法
2018/12/07 Javascript
jquery实现图片放大镜效果
2020/12/23 jQuery
[02:41]DOTA2英雄基础教程 冥魂大帝
2014/01/16 DOTA
跟老齐学Python之画圈还不简单吗?
2014/09/20 Python
基于Python Shell获取hostname和fqdn释疑
2016/01/25 Python
Python性能提升之延迟初始化
2016/12/04 Python
kafka-python批量发送数据的实例
2018/12/27 Python
python实现简易学生信息管理系统
2020/04/05 Python
wxPython窗体拆分布局基础组件
2019/11/19 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
Python读入mnist二进制图像文件并显示实例
2020/04/24 Python
Django通过json格式收集主机信息
2020/05/29 Python
新手学习Python2和Python3中print不同的用法
2020/06/09 Python
出国英文推荐信
2014/05/10 职场文书
项目经理任命书内容
2014/06/06 职场文书
社保代办委托书怎么写
2014/10/06 职场文书
2015年银行柜员工作总结报告
2015/04/01 职场文书
创新创业项目计划书该怎样写?
2019/08/13 职场文书
2019年教师节祝福语精选,给老师送上真诚的祝福
2019/09/09 职场文书
浅谈mysql执行过程以及顺序
2021/05/12 MySQL