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下载文件时显示下载进度的方法
Apr 02 Python
Python实现基于二叉树存储结构的堆排序算法示例
Dec 08 Python
基于Python socket的端口扫描程序实例代码
Feb 09 Python
Python 字符串与数字输出方法
Jul 16 Python
Windows 64位下python3安装nltk模块
Sep 19 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
May 27 Python
聊聊python里如何用Borg pattern实现的单例模式
Jun 06 Python
python隐藏终端执行cmd命令的方法
Jun 24 Python
tensorflow实现打印ckpt模型保存下的变量名称及变量值
Jan 04 Python
Windows下Pycharm远程连接虚拟机中Centos下的Python环境(图文教程详解)
Mar 19 Python
详解python 条件语句和while循环的实例代码
Dec 28 Python
Django如何创作一个简单的最小程序
May 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
PHP将整个网站生成HTML纯静态网页的方法总结
2012/02/05 PHP
PHP中使用memcache存储session的三种配置方法
2014/04/05 PHP
PHP APC配置文件2套和参数详解
2014/06/11 PHP
smarty缓存用法分析
2014/12/16 PHP
PHP实现简单实用的分页类代码
2016/04/08 PHP
php获取数据库中数据的实现方法
2017/06/01 PHP
JQuery 引发两次$(document.ready)事件
2010/01/15 Javascript
基于jquery的让textarea自适应高度的插件
2010/08/03 Javascript
xml文档转换工具,附图表例子(hta)
2010/11/17 Javascript
js String对象中常用方法小结(字符串操作)
2012/01/27 Javascript
JS简单测试循环运行时间的方法
2016/09/04 Javascript
微信小程序 获取相册照片实例详解
2016/11/16 Javascript
JS正则替换去空格的方法
2017/03/24 Javascript
JavaScript中正则表达式使数字、中文或指定字符高亮显示
2017/10/31 Javascript
zepto.js 实时监听输入框的方法
2018/12/04 Javascript
ES6中的class是如何实现的(附Babel编译的ES5代码详解)
2019/05/17 Javascript
axios异步提交表单数据的几种方法
2019/08/11 Javascript
世界上最短的数字判断js代码
2019/09/09 Javascript
微信小程序利用button控制条件标签的变量问题
2020/03/15 Javascript
vue props 一次传多个值实例
2020/07/22 Javascript
解决Vue的项目使用Element ui 走马灯无法实现的问题
2020/08/03 Javascript
[07:57]2018DOTA2国际邀请赛寻真——PSG.LGD凤凰浴火
2018/08/12 DOTA
python的id()函数解密过程
2012/12/25 Python
Python管理Windows服务小脚本
2018/03/12 Python
Django框架模板注入操作示例【变量传递到模板】
2018/12/19 Python
关于python3中setup.py小概念解析
2019/08/22 Python
Django实现WebSSH操作物理机或虚拟机的方法
2019/11/06 Python
html5 canvas的绘制文本自动换行的示例代码
2018/09/17 HTML / CSS
HTML5逐步分析实现拖放功能的方法
2020/09/30 HTML / CSS
护士岗前培训自我评鉴
2014/02/28 职场文书
入党函调证明材料
2014/12/24 职场文书
驳回起诉民事裁定书
2015/05/19 职场文书
故意杀人罪辩护词
2015/05/21 职场文书
当你找不到方向的时候,不妨读读刘备的一生
2019/08/05 职场文书
总结Python使用过程中的bug
2021/06/18 Python
Vue2.0搭建脚手架
2022/03/13 Vue.js