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 25 Python
python中列表元素连接方法join用法实例
Apr 07 Python
Django 忘记管理员或忘记管理员密码 重设登录密码的方法
May 30 Python
python计算两个数的百分比方法
Jun 29 Python
ERLANG和PYTHON互通实现过程详解
Jul 05 Python
Windows10下Tensorflow2.0 安装及环境配置教程(图文)
Nov 21 Python
Python 实现数组相减示例
Dec 27 Python
解决windows下python3使用multiprocessing.Pool出现的问题
Apr 08 Python
Tensorflow使用Anaconda、pycharm安装记录
Jul 29 Python
详解numpy.ndarray.reshape()函数的参数问题
Oct 13 Python
python爬虫中抓取指数的实例讲解
Dec 01 Python
Pytorch中expand()的使用(扩展某个维度)
Jul 15 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修正代码
2011/05/09 PHP
浅析PHP中Session可能会引起并发问题
2015/07/23 PHP
PHP+redis实现添加处理投票的方法
2015/11/14 PHP
基于 Swoole 的微信扫码登录功能实现代码
2018/01/15 PHP
PHP实现Huffman编码/解码的示例代码
2018/04/20 PHP
PHP设计模式入门之迭代器模式原理与实现方法分析
2020/04/26 PHP
jQuery)扩展jQuery系列之一 模拟alert,confirm(一)
2010/12/04 Javascript
js打开windows上的可执行文件示例
2014/05/27 Javascript
用js通过url传参把数据从一个页面传到另一个页面
2014/09/01 Javascript
原生js实现日期联动
2015/01/12 Javascript
js获取当前时间(昨天、今天、明天)
2016/11/23 Javascript
bootstrap+jQuery实现的动态进度条功能示例
2017/05/25 jQuery
使用jQuery实现购物车结算功能
2017/08/15 jQuery
微信小程序实现简单评论功能
2018/11/28 Javascript
Vue实现数据请求拦截
2019/10/23 Javascript
微信小程序中data-key属性之数据传输(经验总结)
2020/08/22 Javascript
python 实现上传图片并预览的3种方法(推荐)
2017/07/14 Python
python实现dijkstra最短路由算法
2019/01/17 Python
Django 实现将图片转为Base64,然后使用json传输
2020/03/27 Python
django 模型字段设置默认值代码
2020/07/15 Python
django有哪些好处和优点
2020/09/01 Python
Clarins娇韵诗美国官网:法国天然护肤品牌
2016/09/26 全球购物
白俄罗斯在线大型超市:e-dostavka.by
2019/07/25 全球购物
法国一家多品牌成衣精品中/高档商店:Graduate Store
2019/08/28 全球购物
英国百年闻名的优质健康产品连锁店:Holland & Barrett
2019/12/19 全球购物
linux面试题参考答案(4)
2014/09/21 面试题
个人实用简单的自我评价
2013/10/19 职场文书
教师年度考核评语
2014/04/28 职场文书
新闻传播专业求职信
2014/07/22 职场文书
部队2015年终工作总结
2015/04/02 职场文书
创业计划书之寿司
2019/07/19 职场文书
导游词之广东佛山(南风古灶)
2019/09/24 职场文书
解决Jenkins集成SonarQube遇到的报错问题
2021/07/15 Java/Android
python周期任务调度工具Schedule使用详解
2021/11/23 Python
Python语言中的数据类型-序列
2022/02/24 Python
漫画「处刑少女的生存之道」第3卷封面公开
2022/03/21 日漫