2016-04-27 46 views
0

我過4列表迭代器編寫循環是這樣的:ValueError異常的StopIteration之前提出地圖(接下來,iterables)

it0 = iter(['foo','bar']) 
it1 = iter(['bar','foo']) 
it2 = iter(['foo','foo']) 
it3 = iter(['bar','bar']) 

try: 
    a, b, c, d = map(next, (it0, it1, it2, it3)) 
    while True: 
     #some operations that can raise ValueError 
     if a+b+c+d == 'foobar'*2: 
      a, b, c, d = map(next, (it0, it1, it2, it3)) 
     elif a+b == 'foobar': 
      c, d = map(next,(it2, it3)) 
     else: 
      a, b = map(next,(it0, it1)) 
except StopIteration: 
    pass 

但是當我運行在Python3這個代碼,我得到前提出了ValueError: not enough values to unpack (expected 2, got 0)預計StopIteration

我不想在except語句中捕獲ValueError,因爲while循環中的某些操作也可能導致應該停止程序的ValueError

next在分配前怎麼沒有提出異常?

在Python2.7中,首先提出StopIteration

+0

你或許應該修正這個錯誤,但FYI你可以趕上一個以上的異常類型,不會夾住東西:'除了StopIteration異常,ValueError異常: do_something()' – jDo

+0

酷酷..你的問題只是讓我相信你不知道這一點。它的寫法就好像你唯一的選擇就是捕獲* StopIteration或者ValueError。不是都。無論如何,我認爲彼得斯先生可能已經解決了下面的*實際問題。 – jDo

+1

@jDo,我只想捕獲StopIteration來停止while循環,但我希望程序在出現ValueError時停止。如果兩者都被捕獲並且ValueError可能意味着StopIteration,則會引起混淆。正如你所說的問題解決了。 –

回答

4

StopIteration升高,但元組分配吞下這個,因爲它看到StopIteration作爲信號從map()迭代器,它完成生產值:

>>> i0, i1 = iter(['foo']), iter([]) 
>>> m = map(next, (i0, i1)) 
>>> next(m) 
'foo' 
>>> next(m) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 
>>> i0, i1 = iter(['foo']), iter([]) 
>>> m = map(next, (i0, i1)) 
>>> a, b = m 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: not enough values to unpack (expected 2, got 1) 

這是正常行爲; Python內置函數期望迭代器作爲輸入始終使用StopIteration作爲「迭代完成」信號,並且元組解包必須在此迭代。

要麼首先map()輸出轉換到一個列表並測試長度,在每個迭代使用next()分開,並且不使用map(),或趕上ValueError本地。

測試長度將不得不重新提高StopIteration

values = list(map(next, (it0, it1, it2, it3))) 
if len(values) < 4: 
    raise StopIteration 
a, b, c, d = values 

注意的是,這裏list()吞下了StopIteration例外。

鳳雲ValueError只是爲map()操作:

try:  
    a, b, c, d = map(next, (it0, it1, it2, it3)) 
except ValueError: 
    raise StopIteration 

不使用map()都通過在每個迭代器調用next()分別:使用列表理解

a, b, c, d = next(it0), next(it1), next(it2), next(it3) 

或:

a, b, c, d = [next(i) for i in (it0, it1, it2, it3)] 

兩者都確保在分配發生之前調用next(),而不是在分配過程中調用。

+0

謝謝,現在它更有意義。請問爲什麼Python2.7的行爲有所不同? –

+2

@JacquesGaudin:'map()'在Python 2.7中返回一個列表。這意味着它必須在元組賦值發生前首先產生該列表,所以'StopIteration'異常在元組賦值迭代之外引發。 –

+0

這使得更有意義!非常感謝您的及時答覆。 –

0

在的Martijn Pieters的答案的光,使用列表理解直接作用:

a, b, c, d = [next(it) for it in (it0, it1, it2, it3)] 

post闡明爲什麼for漁獲StopIteration的迭代,但不是在循環體。

另一種可能的方式,採取default參數next的優勢:

it0 = iter(['foo','bar']) 
it1 = iter(['bar','foo']) 
it2 = iter(['foo','foo']) 
it3 = iter(['bar','bar']) 

try: 
    a, b, c, d = map(next, (it0, it1, it2, it3), (None,)*4) 
    while all(_ is not None for _ in (a,b,c,d)): 
     #some operations that can raise ValueError 
     if a+b+c+d == 'foobar'*2: 
      a, b, c, d = map(next, (it0, it1, it2, it3), (None,)*4) 
     elif a+b == 'foobar': 
      c, d = map(next,(it2, it3), (None,)*2) 
     else: 
      a, b = map(next,(it0, it1), (None,)*2) 
except StopIteration: 
    pass 
+0

我也將這種方法併入了我的答案中;它基本上與在各個迭代器上首先使用next()方法的方法相同,即避免使用生成器推遲使用next()直到迭代進行賦值。 –

+0

當然,值得注意的是,'for'循環體中的'StopIteration'並沒有被吞噬。 –