您的位置:首页 > 编程语言 > Python开发

一段关于Python字典遍历的“争论”

2013-03-05 11:36 190 查看

一段关于Python字典遍历的“争论”

Posted on 2010年10月31日6,249 阅读

小弟我今天吃饱了饭逛大神们的blog,发现bones某篇日志下面这么一段小小的争论。

先摘抄下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#这里初始化一个dict

>>> d=
{'a':1,'b':0,'c':1,'d':0}
#本意是遍历dict,发现元素的值是0的话,就删掉

>>> fork
ind:
...  if
d[k]==
0:

...    del(d[k])
...

Traceback (mostrecent
call last):
  File"<stdin>",
line 1,in
<module>

RuntimeError:
dictionary changedsize
duringiteration
#结果抛出异常了,两个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常量。

>>> fork
ind.keys():
...  if
d[k]==
0:

...    del(d[k])
...

>>> d
{'a':1,
'c':
1}

#结果也是对的
>>>

其实这个问题本来很简单,就是说如果遍历一个字典,但是在遍历中改变了他,比如增删某个元素,就会导致遍历退出,并且抛出一个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]

这个写法让我眼前一亮:怎么还有这个语法?

再仔细一看,他可能是这个意思:

#!/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

1
2
3
4
5
6
7
8
9
10

#!/usr/bin/env python

# -*- coding=utf-8 -*-
a=
{'a':1,'b':0,'c':1,'d':0}

b={}
fork,vin
a.items():

    if v!=
0:
        b.update({k:v})

adict =b
delb

print a

不知道对不对。

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

val = float(raw_input("Age: "))
status = ("working","retired")[val>65]
print "You should be",status

1
2
3

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'

1
2
3
4
5

#V1 if X else V2

s =None
a=
"not null"if
s==
Noneelse
s

print a
#'not null'

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

>>> 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 }

也就是赖勇浩原文里面提到的语法

原文:http://blog.ihipop.info/2010/10/1777.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: