2016-04-27 10 views
0

私はこのような4リスト・イテレータをループを書いていますマップを呼び出すとStopIteration前に提起:とValueError(次、イテラブル)

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'のいずれかを捕捉するように書かれています。両方ではありません。とにかく、Pieters氏が実際の問題を以下のように解決したと思います。 – jDo

+1

@jDo、私はちょうどループを停止するためにStopIterationをキャッチしたいですが、私はValueErrorがある場合、プログラムを停止したいです。 ValueErrorがStopIterationを意味する可能性がある場合は、混乱します。あなたは問題が解決したと言った。 –

答えて

4

StopIterationが高く、それはそれが値を生成行われことmap()イテレータからの信号としてStopIterationを見るようタプル割り当てこれを飲み込むれる:

>>> 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()出力を変換して長さをテストし、それぞれ別々なく局所map()、又はキャッチValueErrorを使用する反復可能にnext()を使用します。

長さをテストするStopIterationを再調達する必要があります:ここlist()StopIteration例外を飲み込んだ

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

注意を。ただmap()操作のためValueErrorをキャッチ

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

は、個別に各イテレータにnext()を呼び出すことによって、全くmap()を使用していない:

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

、:

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

このpostは、ループの本体で反復可能ではなく、上の理由forキャッチStopIterationを明確にしています。

もう一つの可能​​な方法、nextdefaultパラメータを生かし:

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'が飲み込まれていないことに注意してください。 –