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 を使うと、その値が StopIterationvalue 属性に格納されます。

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 と組み合わせて使われることがあります。