Pythonジェネレーターでfor… elseを使用する
質問
私はPythonの for ... else構文の大ファンです-どれくらいの頻度で適用できるか、そしてコードをどれだけ効果的に簡素化できるかは驚くべきことです。
しかし、ジェネレータでそれを使用する良い方法を見つけていません。例えば:
def iterate(i):
for value in i:
yield value
else:
print 'i is empty'
上記の例では、 i
が空の場合にのみ print
ステートメントが実行されるようにします。ただし、 else
は break
および return
のみを尊重するため、 i
の長さに関係なく、常に実行されます。
この方法で for ... else
を使用することが不可能な場合、何も得られない場合にのみ print
ステートメントが実行されるようにする最善の方法は何ですか?
解決
ジェネレーターの定義を壊しているため、反復が完了するとStopIteration例外がスローされます(ジェネレーター関数のreturnステートメントによって自動的に処理されます)
だから:
def iterate(i):
for value in i:
yield value
return
呼び出し元のコードが空のイテレータのケースを処理できるようにするのに最適:
count = 0
for value in iterate(range([])):
print value
count += 1
else:
if count == 0:
print "list was empty"
上記をよりクリーンに行う方法かもしれませんが、それはうまく動作するはずであり、一般的な「リストのようなイテレータの処理」トラップのいずれにも該当しません。
他のヒント
これを行うにはいくつかの方法があります。常に Iterator
を直接使用できます:
def iterate(i):
try:
i_iter = iter(i)
next = i_iter.next()
except StopIteration:
print 'i is empty'
return
while True:
yield next
next = i_iter.next()
しかし、引数 i
に何を期待するかについてもっと知っているなら、もっと簡潔にすることができます:
def iterate(i):
if i: # or if len(i) == 0
for next in i:
yield next
else:
print 'i is empty'
raise StopIteration()
以前の回答の一部をまとめると、次のように解決できます:
def iterate(i):
empty = True
for value in i:
yield value
empty = False
if empty:
print "empty"
したがって、実際には" else"はありません。関連する条項。
ご指摘のとおり、 for..else
は break
のみを検出します。そのため、何かを探してから停止する場合にのみ適用されます。
ジェネレータではないので目的に当てはまりませんが、すべての要素を停止せずに処理したいので(すべての要素を生成したいので、それはポイントではありません)。
つまりジェネレーターであるかどうかにかかわらず、Berのソリューションのようにブール値が本当に必要です。
for ... elseをこの方法で使用することが不可能な場合、printステートメントは何も生成されない場合にのみ実行されるようにするための最善のアプローチは何ですか?
考えられる最大:
>>> empty = True
>>> for i in [1,2]:
... empty = False
... if empty:
... print 'empty'
...
>>>
>>>
>>> empty = True
>>> for i in []:
... empty = False
... if empty:
... print 'empty'
...
empty
>>>
単純なif-elseはどうですか?
def iterate(i):
if len(i) == 0: print 'i is empty'
else:
for value in i:
yield value