一段关于Python字典遍历的“争论”
Post by
yaohuaq
  2011-03-11 10:54:17 Friday Tags:python

转:http://ihipop.info/2010/10/1777.html

Python代码
  1. #这里初始化一个dict   
  2. >>> d = {'a':1, 'b':0, 'c':1, 'd':0}   
  3. #本意是遍历dict,发现元素的值是0的话,就删掉   
  4. >>> for k in d:   
  5. ...   if d[k] == 0:   
  6. ...     del(d[k])   
  7. ...   
  8. Traceback (most recent call last):   
  9.   File "<stdin>", line 1, in <module>   
  10. RuntimeError: dictionary changed size during iteration   
  11. #结果抛出异常了,两个0的元素,也只删掉一个。   
  12. >>> d   
  13. {'a': 1, 'c': 1, 'd': 0}   
  14.     
  15. >>> d = {'a':1, 'b':0, 'c':1, 'd':0}   
  16. #d.keys() 是一个下标的数组   
  17. >>> d.keys()   
  18. ['a''c''b''d']   
  19. #这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。   
  20. >>> for k in d.keys():   
  21. ...   if d[k] == 0:   
  22. ...     del(d[k])   
  23. ...   
  24. >>> d   
  25. {'a': 1, 'c': 1}   
  26. #结果也是对的   
  27. >>>  

 

其实这个问题本来很简单,就是说如果遍历一个字典,但是在遍历中改变了他,比如增删某个元素,就会导致遍历退出,并且抛出一个dictionary changed size during iteration的异常
bones的解决方法是遍历字典键值,以字典键值为依据遍历,这样改变了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]

 

这个写法让我眼前一亮:怎么还有这个语法?
再仔细一看,他可能是这个意思:

 

Python代码
  1. #!/usr/bin/env python   
  2. # -*- coding=utf-8 -*-   
  3. a = {'a':1, 'b':0, 'c':1, 'd':0}   
  4. b={}   
  5. for k,v in a.items():   
  6.     if v != 0:   
  7.         b.update({k:v})   
  8. adict = b   
  9. del b   
  10. print a  

不知道对不对。
因为这个写法一开始让我猛然想到三元操作符,仔细一看才发现不是,以前Goolge到有个解决方案

 

Python代码
  1. val = float(raw_input("Age: "))   
  2. status = ("working","retired")[val>65]   
  3. print "You should be",status  

val>65是个逻辑表达式,返回0或者1,刚好作为前面那个元组的ID来取值,实在是太妙了。。。
不过在Google的资料里面还有一个版本

后来发帖在华蟒用户组(中文Python技术邮件列表)http://groups.google.com/group/python-cn/browse_thread/thread/047b1401e96c4b88#)中提到后众多大神解答如下:

 

Python代码
  1. >>> alist = [1,2,0,3,0,4,5]   
  2. >>> alist = [i for i in alist if i != 0]   
  3. >>> alist   
  4.   
  5. [1, 2, 3, 4, 5]   

 

 

Python代码
  1. >>> d = {'a':1, 'b':0, 'c':1, 'd':0}   
  2. >>> d = dict([(k,v) for k,v in d.iteritems() if v!=0])   
  3. >>> d   
  4. {'a':1,'c':1'}   

如果大于Python>=2.7
还可以用这个写法:

 

PHP代码
  1. >>> d = {k:v for k,v in d.iteritems() if v !=0 }   
发表评论:
姓名:
联系方法:(选填)
评论内容:
个人信息
欠X的生活
昵称:c77cc
QQ:
Email:yaohuaq#gmail.com
居住地:北京 海淀
星座:
毕业院校:
工作点:lighthonor.com