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网站验证码识别
Jan 25 Python
Python增量循环删除MySQL表数据的方法
Sep 23 Python
python分析作业提交情况
Nov 22 Python
基于并发服务器几种实现方法(总结)
Dec 29 Python
Python基于TCP实现会聊天的小机器人功能示例
Apr 09 Python
Python多进程写入同一文件的方法
Jan 14 Python
pytorch masked_fill报错的解决
Feb 18 Python
Pycharm 使用 Pipenv 新建的虚拟环境(图文详解)
Apr 16 Python
python3 使用openpyxl将mysql数据写入xlsx的操作
May 15 Python
如何对python的字典进行排序
Jun 19 Python
通俗讲解python 装饰器
Sep 07 Python
Python中super().__init__()测试以及理解
Dec 06 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实现提取一个图像文件并在浏览器上显示的代码
2012/10/06 PHP
PHP保留两位小数并且四舍五入及不四舍五入的方法
2013/09/22 PHP
PHP 登录完成后如何跳转上一访问页面
2014/01/14 PHP
php获取文件大小的方法
2014/02/26 PHP
ThinkPHP实现带验证码的文件上传功能实例
2014/11/01 PHP
两个php日期控制类实例
2014/12/09 PHP
PHP使用缓存即时输出内容(output buffering)的方法
2015/08/03 PHP
php使用fputcsv实现大数据的导出操作详解
2020/02/27 PHP
JavaScript 入门·JavaScript 具有全范围的运算符
2007/10/01 Javascript
javascript 放大镜效果js组件 qsoft.PopBigImage.v0.35 加入了chrome支持
2009/04/07 Javascript
javascript实现十六进制颜色值(HEX)和RGB格式相互转换
2014/06/20 Javascript
通过location.replace禁止浏览器后退防止重复提交
2014/09/04 Javascript
JavaScript 对象深入学习总结(经典)
2015/09/29 Javascript
JS实现的自定义网页拖动类
2015/11/06 Javascript
JavaScript+html5 canvas绘制的圆弧荡秋千效果完整实例
2016/01/26 Javascript
Node.JS中事件轮询(Event Loop)的解析
2017/02/25 Javascript
探索webpack模块及webpack3新特性
2017/09/18 Javascript
Node.js利用断言模块assert进行单元测试的方法
2017/09/28 Javascript
详解vue-cli项目中用json-sever搭建mock服务器
2017/11/02 Javascript
Angular2整合其他插件的方法
2018/01/20 Javascript
在vue项目中使用Nprogress.js进度条的方法
2018/01/31 Javascript
vue+web端仿微信网页版聊天室功能
2019/04/30 Javascript
vue+导航锚点联动-滚动监听和点击平滑滚动跳转实例
2019/11/13 Javascript
javascript实现贪吃蛇游戏(娱乐版)
2020/08/17 Javascript
JS绘图Flot如何实现动态可刷新曲线图
2020/10/16 Javascript
js实现限定区域范围拖拉拽效果
2020/11/20 Javascript
[02:57]DOTA2亚洲邀请赛 SECRET战队出场宣传片
2015/02/07 DOTA
Python学习笔记(一)(基础入门之环境搭建)
2014/06/05 Python
Python中shutil模块的学习笔记教程
2017/04/04 Python
环保建议书300字
2014/05/14 职场文书
廉洁自律演讲稿
2014/05/22 职场文书
2014广电局实施党的群众路线教育实践活动方案思想汇报
2014/09/22 职场文书
员工评语范文
2014/12/31 职场文书
幼儿教师继续教育培训心得体会
2016/01/19 职场文书
如何利用opencv判断两张图片是否相同详解
2021/07/07 Python
logback如何自定义日志存储
2021/08/30 Java/Android