Python __next__とは?イテレータを自作する方法とStopIteration
__next__は、イテレータが「次の値」を返すための特殊メソッドです。
next()関数やfor文の1周ごとに呼ばれ、終わりはStopIterationで伝えます。
__next__の基本
class Lines:
def __init__(self, text):
self.rows = text.splitlines()
self.i = 0
def __iter__(self):
return self
def __next__(self):
if self.i >= len(self.rows):
raise StopIteration
row = self.rows[self.i]
self.i += 1
return row
it = Lines("a\nb")
print(next(it))
print(next(it))
next(it)
実行結果:
a
b
StopIteration
イテレータには2つのメソッドが必要です。
__next__—— 次の値を返す__iter__——selfを返す
__iter__を忘れるとfor文が動かない
class OnlyNext:
def __next__(self):
return 1
for x in OnlyNext():
print(x)
実行結果:
TypeError: 'OnlyNext' object is not iterable
for文は最初にiter()を呼ぶので、__next__だけでは足りません。
逆に__iter__だけでも失敗します。
class NoNext:
def __iter__(self):
return self
next(NoNext())
実行結果:
TypeError: 'NoNext' object is not an iterator
「is not an iterator」は__next__が無いという意味です。「is not iterable」(__iter__が無い)と1語違いで意味が違うので読み分けてください。
StopIterationを出さないと止まらない
class Never:
def __iter__(self):
return self
def __next__(self):
return 1
list(Never()) # ← 終わらない
StopIterationを投げない限り、for文は永久に回り続けます。
途中で打ち切って確認するとこうなります。
import itertools
print(list(itertools.islice(Never(), 3)))
実行結果:
[1, 1, 1]
無限イテレータ自体は正当な設計です(itertools.count()など)。止める気があるなら必ずraise StopIterationを書いてください。
next()には既定値を渡せる
it = Lines("")
print(next(it, "終わり"))
実行結果:
終わり
第2引数を渡すと、StopIterationの代わりにその値が返ります。
tryで囲む必要がなくなるため、1つだけ取り出したいときに便利です。
普通はyieldで書けばよい
ここまでの内容を、yieldならこう書けます。
class Lines:
def __init__(self, text):
self.text = text
def __iter__(self):
for row in self.text.splitlines():
yield row
print(list(Lines("a\nb")))
print(list(Lines("a\nb")))
実行結果:
['a', 'b']
['a', 'b']
位置を管理する変数もStopIterationも不要になり、しかも何度でも回せます。
手書きの__next__が必要なのは、次のような場合に限られます。
- 外部から1つずつ取り出す制御が必要
- ファイルや通信のように本質的に使い捨て
- イテレータ自体にメソッドや属性を持たせたい
それ以外は__iter__とyieldで十分です。「Python __iter__とは?for文で回せるクラスを作る方法」を参照してください。
ジェネレータのreturnはStopIterationになる
def g():
yield 1
return "戻り値"
it = g()
print(next(it))
try:
next(it)
except StopIteration as e:
print("value:", e.value)
実行結果:
1
value: 戻り値
ジェネレータのreturnは、StopIterationのvalue属性として運ばれます。
for文では捨てられるので、通常は気にする必要はありません。
__next__のまとめ
- 次の値を返す——
next()とfor文から呼ばれる __iter__でselfを返すのがセット- 「is not an iterator」=
__next__が無い StopIterationを出さないと無限ループnext(it, 既定値)で例外を避けられる- 大半の用途は
yieldのほうが簡単で安全







