2013-01-22 24 views
1
a = [0,1,2,3,4,5] 
for b in a: 
    print ":"+str(b) 
    a.pop(0) 

認爲這將工作以便通過整個列表及其所有項目我運行此代碼,並期望這一點。Python for循環列表有趣的結果

:0 
0 
:1 
1 
:2 
2 
:3 
3 
:4 
4 
:5 
5 

相反,我得到這個:

:0 
0 
:2 
1 
:4 
2 

現在我明白爲什麼會這樣,但是這是在Python錯誤?難道它不應該通過所有的原始對象而不是當前列表的長度?爲什麼這不會拋出錯誤? IE:它不應該仍然這樣做:

:0 
0 
:1 
2 
:2 
4 
:3 
Error 
:4 
Error 
:5 
Error 

回答

4

您遍歷一個列表,並在同一時間改變它。通過使用.pop()您縮短了列表,但迭代器指針未更新。

使用複製代替:

for b in list(a): 

for b in a[:]: 

其中[:]片記號返回一個列表副本。

另一種方法是使用while循環,而不是:

while a: 
    print a.pop(0) 

因爲空單測試作爲布爾False

python for循環使用它的參數作爲迭代器,它本身不保存索引。 for循環無法「知道」已刪除的元素。相反,它是list()迭代器,它可將指針:

>>> a = [0,1,2,3,4,5] 
>>> itera = iter(a) 
>>> itera.next() # index 0 -> a[0] is 0 
0 
>>> a.pop(0) 
0 
>>> a 
[1,2,3,4,5] 
>>> itera.next() # index 1 -> a[1] is 2 
2 

這個迭代保持一個計數器,每當你對迭代器調用next()時候它會給你下一個索引,無論該值可以在值爲,直到計數器等於列表中的當前的長度。

+0

我已經明白了爲什麼,但不應該拋出一個錯誤? – gabeio

+0

@gabeDel:你爲什麼期望出現錯誤? –

+0

哦,我忘記了名單正在縮短,但它仍然是由列表的長度,而不是每個元素。 – gabeio

5

這是完全「預期」和記錄的行爲。當你遍歷列表時,你基本上遍歷內存位置。當你從列表中彈出一些東西時,列表中的所有內容都會將1索引移動到靠近列表開頭的位置。因此,你最終會跳過項目。到達列表末尾時迭代停止。

通常做這樣的事情的時候,你要遍歷列表的副本:

for b in a[:]: 
    ... 

正如評論所指出的,如果你遍歷以相反的順序列表:

for b in reversed(a): 
    a.pop() 

這可以按照預期工作,因爲您不斷拉扯最終元素,因此您不會將位置移到您尚未看到的任何元素的列表中。

+0

在某些情況下另一種有用的方法是以相反的順序進行迭代:'for b in reverse(a)' – kindall

+0

@kindall - 感謝您的評論。我已將這些信息納入我的答案。 – mgilson

+0

-1是'.pop()'的默認參數,因此您可以安全地忽略它。 –

0

如果你想在一個循環中使用.pop(),一個常見的成語是與while使用它:

a = [0,1,2,3,4,5] 
while a: 
    print ":{}".format(a.pop(0)) 

或者,如果你想打印的圖案,你必須有:

a = [0,1,2,3,4,5] 
while a: 
    print ":{}\n{}".format(a[0],a.pop(0)) 

打印

:0 
0 
:1 
1 
:2 
2 
:3 
3 
:4 
4 
:5 
5 
0

在的每次迭代中0環,我們必須檢查車況b in a所以,當你開始:

a = [0,1,2,3,4,5] 
for b in a: 
    print ":"+str(b) 
    a.pop(0) 

b = 0in a(在意義的元素)是5。現在,您打印字符串版本a[b],然後刪除數組的第一個元素。因此迭代二變成了:

a = [1, 2, 3, 4, 5] 
b = 1 (it incremented) 
size of a = 4 (it shrank) 

接下來他們將b=2,的尺寸變成3.這將產生出界你期待的錯誤,因爲b將不會再發生的最後一次迭代大於數組的大小,所以我們完成了。