熱線電話:13121318867

登錄
首頁精彩閱讀Python中遍歷字典過程中更改元素導致異常的解決方法
Python中遍歷字典過程中更改元素導致異常的解決方法
2017-09-03
收藏

Python中遍歷字典過程中更改元素導致異常的解決方法

這篇文章主要介紹了Python中遍歷字典過程中更改元素導致錯誤的解決方法,針對增刪元素后出現dictionary changed size during iteration的異常解決做出討論和解決,需要的朋友可以參考下.

先來回顧一下Python中遍歷字典的一些基本方法:
腳本:    

執行結果:    
##########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 }

數據分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數據分析師資訊
更多

OK
客服在線
立即咨詢
日韩人妻系列无码专区视频,先锋高清无码,无码免费视欧非,国精产品一区一区三区无码
客服在線
立即咨詢