2017-09-07 138 views
4

最近我學習了關於列表和循環的知識,以及指示並刪除列表中最後一項的命令.pop()Python「for in」循環打印列表中的最後一項

因此,我試圖編寫一個代碼,逐個刪除列表中的最後一個項目,直到只剩下一個項目。

的代碼是:

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 

for i in list_A: 
    print(list_A.pop()) 
    if 'c' not in list_A: 
     break 

print("job done.") 

蟒蛇3.6的輸出給了我這樣的:

/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6 
j 
i 
h 
g 
f 
job done. 

正如你所看到的,它實際工作,但它的一半?

我期待:

j 
i 
h 
g 
f 
e 
d 
c 
job done 

我的意思是,我會,如果返回某些錯誤更舒適,這意味着該代碼是不正確的。但爲什麼它能夠工作,但不是一個完整的方式?

+0

你沒有得到你的預期輸出的原因是因爲你在迭代它的同時修改迭代器。 –

+0

不要'list_A.pop()'。 – DyZ

+0

夥計們,非常感謝!我的確在玩耍,看看這些事情是如何一起工作的。顯然,我不熟悉「迭代」,我明白一個while循環現在是更好的選擇。 –

回答

5

您在迭代列表時突變列表。

可以使用while循環來做到這一點:

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 

while 'c' in list_A: 
    print(list_A.pop()) 

print('job done') 

輸出:

j 
i 
h 
g 
f 
e 
d 
c 
job done 

一個更有效的方法是確定標記字符的第一個實例的索引,刪除它和列表的其餘部分(雖然字符不會被打印,因爲它們被刪除):

try: 
    pos = list_A.index('c') 
    list_A[:] = list_A[:pos] 
    # del list_A[pos:]   # more efficient alternative suggested by @ShadowRanger 
except ValueError as e: 
    pass 
+0

當然,您可以隨時在零件被移除之前將零件切片並打印出來。 'remove = list_A [pos:]','del list_A [pos:]'(使用'del'避免使臨時參與的'list_A [:] = list_A [:pos]'),' ):print(x)' – ShadowRanger

+0

這會檢查每個迭代中的'c'是否在列表中,爲什麼不能循環最大'n'次而不是'n²*(n + 1)/ 2'。 –

+0

@ShadowRanger:是的,'del'更好。 – mhawke

1
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 

while list_A: # while list_A has elements, in case 'c' wasn't present 
    el = list_A.pop() # save the last element 
    print(el) 
    if 'c'==el: # if 'c' was popped (reached) 
     break 
print("job done.") 

這樣,即使'c'不存在,它只會打印所有內容然後退出。這也避免了檢查每次迭代是否存在'c',這需要時間。

基於@ MSeifert的評論,如果循環不應該停止在代替殺出第一c停止每當列表沒有c,稍加修改,以上面的代碼中的結果:

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'c', 'h', 'i', 'j'] 

while list_A: 
    print(list_A.pop()) 
    if 'c' not in list_A: 
     break 
print("job done.") 

我們能走得更快,但我不知道無論您學到列表切片和comprehesion然而,這裏有一個更好更快的解決方案:

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 
try: 
    p=list_A.index('c') 
    r='\n'.join(list_A[list_A.index('c'):][::-1]) 
except ValueError: 
    r='\n'.join(list_A[::-1]) 
print(r) 
print('job done.') 
+0

@HubertGrzeskowiak當'c''不存在時,我不會出現異常,因爲那麼數組將被打印並清空,因此'while list_A:'將計算爲'False',因此循環終止。 –

+0

你說得對。我的錯。我想我把它與其他答案混合起來。我刪除了不必要的評論。 –

+3

這個答案依賴於它應該停在第一個彈出的'c'的假設,而問題包含的代碼將表明它應該停止,只要列表中沒有''c''。對於一個'c''是等價的,但如果它包含多個(或不包含),結果將會不同。 – MSeifert

3

當使用Python中的for..in循環,你不應該修改名單。

這裏發生了什麼事是這樣的:

  • 環路從第一項目開始到最後,所以它在第一循環迭代開始於a
  • pop()移除最後一個列表項,那麼你擺脫最後一個字母j並打印它
  • 這一切都繼續爲接下來的5個字母。您遍歷它們從左邊,但在同一時間
  • 除去在右邊的最後一個在會見e您刪除,然後從列表打印f
  • 後,該列表包含字母ae和自你只是遍歷e循環的工作已經完成

這真的很難說你想在這裏做什麼,因爲它更多的玩弄,而不是得到的東西做。不過,無論何時打算從循環內編輯列表,我都會建議使用while循環。你有正確的語義例子看起來是這樣的:

while list_A: 
    print(list_A.pop()) 
    if "c" not in list_A: 
     break 

這個循環去,只要有列表中的項目,只停一次出現在列表中沒有c了。

+0

非常感謝您的快速回答。 我明白你所說的一切,除了'迭代'這個詞。我會嘗試谷歌瞭解這一點。 我不得不說,我確實有點混淆for循環和while循環之間。正如你所提到的for循環不應該修改列表。那麼這兩個循環命令之間有什麼其他建議?我如何快速確定在任何場合使用哪一個? –

+0

這會檢查每個迭代中的「c」是否在列表中,爲什麼不能循環最大n次而不是'n2 *(n + 1)/ 2'。 –

+0

@MrGeek對於這不是最高性能的解決方案,你絕對正確。但是,在這種情況下,我想盡可能保持簡單,因爲OP只是學習Python。 –

2

我最近回答了一個類似的問題,它歸結爲:不要修改你正在迭代的序列。

使用自定義的迭代器(從another answer of mine)顯示發生了什麼:

class CustomIterator(object): 
    def __init__(self, seq): 
     self.seq = seq 
     self.idx = 0 

    def __iter__(self): 
     return self 

    def __next__(self): 
     print('give next element:', self.idx) 
     for idx, item in enumerate(self.seq): 
      if idx == self.idx: 
       print(idx, '--->', item) 
      else: 
       print(idx, ' ', item) 
     try: 
      nxtitem = self.seq[self.idx] 
     except IndexError: 
      raise StopIteration 
     self.idx += 1 
     return nxtitem 

    next = __next__ # py2 compat 

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 

for i in CustomIterator(list_A): 
    print(list_A.pop()) 
    if 'c' not in list_A: 
     break 

它打印:

give next element: 0 
0 ---> a 
1  b 
2  c 
3  d 
4  e 
5  f 
6  g 
7  h 
8  i 
9  j 
j 
give next element: 1 
0  a 
1 ---> b 
2  c 
3  d 
4  e 
5  f 
6  g 
7  h 
8  i 
i 
give next element: 2 
0  a 
1  b 
2 ---> c 
3  d 
4  e 
5  f 
6  g 
7  h 
h 
give next element: 3 
0  a 
1  b 
2  c 
3 ---> d 
4  e 
5  f 
6  g 
g 
give next element: 4 
0  a 
1  b 
2  c 
3  d 
4 ---> e 
5  f 
f 
give next element: 5 
0  a 
1  b 
2  c 
3  d 
4  e 

所以它並沒有因爲break的結束,但因爲它遍歷整個列表(或更好:直到沒有更多項目!)。

另外'c' not in listAO(n)操作,所以你的循環有效O(n**2)。爲什麼不找'c'第一指標和簡單的重複,直到你在那裏:

list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 

try: 
    c_index = list_A.index('c') 
except ValueError: 
    # no 'c' in the list, probably should do something more useful here ... 
    pass 
else: 
    for item in reversed(list_A[c_index:]): # print the items 
     print(item) 
    del list_A[c_index:] # remove the items from the list 

印刷品(如預期):

j 
i 
h 
g 
f 
e 
d 
c 
+1

啊,自定義迭代器的好主意。 +1真的有助於展示_why_修改迭代器,同時迭代它是一個壞主意。 –