2014-01-26 26 views
2

代碼迴路彈出一個元素髮生了什麼:當在名單

arr = [ i for i in xrange(10) ] 

for i in arr: 
    if i in arr: 
    print i 
    arr.pop(0) 

print arr 

和輸出:

$ python2.7 ts.py 
0 
2 
4 
6 
8 
[5, 6, 7, 8, 9] 

爲什麼是這樣的結果?應該不是[]

回答

3

更新序列時迭代有一些意想不到的結果,這就是爲什麼它從不被推薦。下圖描述了可變i如何改變你每次迭代而從列表中

var Instruction     <--------- arr -------------> 
i         [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
|  for i in arr     ^
|____________________________________| 
|         | 
|         V 
|  arr.pop(0)     [1, 2, 3, 4, 5, 6, 7, 8, 9] 
|  
|  for i in arr     [1, 2, 3, 4, 5, 6, 7, 8, 9] 
|          ^
|_______________________________________| 
|_______________________________________| 
|          | 
|          V 
|  arr.pop(0)     [2, 3, 4, 5, 6, 7, 8, 9] 
|  
|  for i in arr     [2, 3, 4, 5, 6, 7, 8, 9] 
|          ^
|__________________________________________| 
|__________________________________________| 
|           | 
|           V 
|  arr.pop(0)     [3, 4, 5, 6, 7, 8, 9] 
|  
|  for i in arr     [3, 4, 5, 6, 7, 8, 9] 
|           ^
|_____________________________________________| 
|_____________________________________________| 
|            | 
|            V 
|  arr.pop(0)     [4, 5, 6, 7, 8, 9] 
|  
|  for i in arr     [4, 5, 6, 7, 8, 9] 
|            ^
|________________________________________________| 
|________________________________________________| 
|            | 
|            V 
|  arr.pop(0)     [5, 6, 7, 8, 9] 
4

建議您在迭代序列(或映射)時不要修改序列(或映射)。它會混淆interal指針。

例如,下面的代碼將永遠不會結束。

arr = [1,2,3] 
for i in arr: 
    print i 
    arr.append(i) 

根據for statement - NOTE

注:有序時,正在由 環改性(這僅可導致產生可變的序列,即列表)一個微妙。一個 內部計數器用於跟蹤下一個使用的項目,並且 在每次迭代時遞增。當此計數器達到 時,循環終止的序列長度。 這意味着如果 套件從序列中刪除當前(或前一個)項目,則會跳過下一項目(因爲它獲取已處理的當前項目 的索引)。同樣,如果套件在當前項目之前的序列中插入 項目,則當前項目將在下次通過循環時再次處理 。這可能會導致可以通過使使用的 片的整個序列的臨時副本,比如避免討厭的 錯誤,

for x in a[:]: 
    if x < 0: a.remove(x) 
1

你whanted是顯然的:

for i in range(len(arr)): 
    arr.pop(0) 

因爲,正如@falsetru提到的那樣,不建議在迭代過程中更改序列,但在本例中,for循環基於一個常量值,即長度爲arr。每個pop()將刪除第一個元素,並且列表將逐漸清空。

1

它更容易看到最新加入的枚舉去上彈出時間:

for index,i in enumerate(arr): 
    if i in arr: 
     print(index,i) 
     arr.pop(0) 
print arr 

輸出:

(0, 0) 
(1, 2) 
(2, 4) 
(3, 6) 
(4, 8) 
[5, 6, 7, 8, 9] 

Popping更改數組的長度,但迭代器的索引未更新以反映此情況。

1

讓我告訴你的代碼會發生什麼:

# Initial position 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
#^
# i 

# Remove first 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
#^
# i 

# Move next 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
# ^
# i 

# Remove first 
[2, 3, 4, 5, 6, 7, 8, 9] 
# ^
# i 

# Move next 
[2, 3, 4, 5, 6, 7, 8, 9] 
#  ^
#  i 

# And so on... 

[4, 5, 6, 7, 8, 9] 
#   ^
#    i 

# Remove first 
[5, 6, 7, 8, 9] 
#   ^
#    i 

# Move next 
# Oops, the end of the list 

# The result: 

[5, 6, 7, 8, 9] 

讓我們來看看它是如何引擎蓋下工作。首先,我們需要一個迭代器:

# for i in arr: 

In [30]: it = iter(arr) 

In [31]: it 
Out[31]: <listiterator at 0x130f9d0> 

我們將稱之爲next(it),直到它上升一個StopIteration例外。所以,讓我們來做:

In [32]: i = next(it) 

In [33]: i 
Out[33]: 0 

哇,我們從列表中獲得了第一個元素!讓我們看看,如果我們用零指數流行元素會發生什麼:

# if i in arr: 
#  print i 
#  arr.pop(0) 

In [34]: i in arr 
Out[34]: True 

In [35]: print i 
0 

In [36]: arr.pop(0) 
Out[36]: 0 

In [37]: arr 
Out[37]: [1, 2, 3, 4, 5, 6, 7, 8, 9] 

好吧,元素已彈出。移動到下一個循環迭代:

In [38]: i = next(it) 

In [39]: i 
Out[39]: 2 

嗯......看起來沒錯,我們得到了第二個元素。讓我們再次彈出第一個!

In [40]: i in arr 
Out[40]: True 

In [41]: print i 
2 

In [42]: arr.pop(0) 
Out[42]: 1 

In [43]: arr 
Out[43]: [2, 3, 4, 5, 6, 7, 8, 9] 

讓我們在第三次迭代看到:

In [44]: i = next(it) 

In [45]: i 
Out[45]: 4 

我想,它現在是明確的,即循環將有5次迭代。在每次迭代中,您將刪除第一個元素。而且,正因爲如此,您將在迭代時跳過奇怪的元素。