Python の StopIteration とは
StopIteration は、イテレータが終端に達したことを示す例外です。普段は for 文が自動的に処理してくれるため意識することは少ないですが、イテレータを手動で操作する際には重要な概念です。
StopIteration とは
イテレータの next() を呼び出し続けると、要素がなくなった時点で StopIteration 例外が発生します。
numbers = [1, 2]
it = iter(numbers)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # StopIteration 例外!
実行すると以下のようなエラーメッセージが表示されます。
Traceback (most recent call last):
File "...", line 5, in <module>
print(next(it))
StopIteration
for 文と StopIteration
for 文は内部で StopIteration をキャッチしてループを終了します。だから普段は意識する必要がありません。
# この for 文は...
for n in [1, 2, 3]:
print(n)
# 内部的にはこう動いている
it = iter([1, 2, 3])
while True:
try:
n = next(it)
print(n)
except StopIteration:
break
手動で StopIteration を扱う
next() のデフォルト値を使わない場合は、try-except で捕捉します。
numbers = [1, 2]
it = iter(numbers)
while True:
try:
n = next(it)
print(n)
except StopIteration:
print("イテレータが終了しました")
break
ジェネレータと StopIteration
ジェネレータ関数が終了すると、自動的に StopIteration が発生します。
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
# 関数終了時に StopIteration が自動発生
gen = count_up_to(2)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # StopIteration
StopIteration に値を持たせる
StopIteration は値を持つことができます。ジェネレータで return を使うと、その値が StopIteration の value 属性に格納されます。
def gen_with_return():
yield 1
yield 2
return "完了"
g = gen_with_return()
print(next(g)) # 1
print(next(g)) # 2
try:
next(g)
except StopIteration as e:
print(e.value) # 完了
この仕組みは、コルーチンや yield from と組み合わせて使われることがあります。