Python | OOP:next

Python Python
スポンサーリンク

概要(nextは「次の要素」を返し、尽きたらStopIterationで終了する)

nextは“イテレータ”が持つ特別メソッドで、呼ぶたびに次の要素を返します。要素がもうないときはStopIterationを送出して、反復を終わらせます。for文や内蔵関数next()の裏側で必ず使われるため、反復処理の芯になる仕組みです。初心者は「イテラブル=iterを持つ」「イテレータ=iternextを持つ」と覚えると整理が速いです。


基本の使い方(イテレータの定義とnext()の動き)

最小のイテレータ実装

class Countdown:
    def __init__(self, start: int):
        self.cur = start  # 3なら 3, 2, 1 を返す

    def __iter__(self):
        return self  # 自分自身がイテレータ

    def __next__(self) -> int:
        if self.cur <= 0:
            raise StopIteration
        v = self.cur
        self.cur -= 1
        return v

c = Countdown(3)
print(next(c))  # 3
print(next(c))  # 2
print(next(c))  # 1
# print(next(c))  # StopIteration(尽きたら終了)
Python

next(obj)は内部でobj.next()を呼びます。要素が尽きたら必ずStopIterationを送出するのが反復の合図です。

for文とnextの関係

for x in Countdown(3):
    print(x)  # 3, 2, 1 を順に表示
Python

forは、イテラブルからイテレータを取得し、nextを繰り返し呼び、StopIterationでループを終わらせます。


イテラブルとの連携(iterで“新しいイテレータ”を返す)

イテラブルとイテレータを分けて設計する

class RangeLike:
    def __init__(self, start: int, stop: int):
        self.start = start
        self.stop = stop

    def __iter__(self):
        return RangeIter(self.start, self.stop)

class RangeIter:
    def __init__(self, start: int, stop: int):
        self.cur = start
        self.stop = stop

    def __iter__(self):
        return self

    def __next__(self) -> int:
        if self.cur >= self.stop:
            raise StopIteration
        v = self.cur
        self.cur += 1
        return v

r = RangeLike(2, 5)
print(list(r))  # [2, 3, 4]
print(list(r))  # 何度でも回せる(毎回新しいイテレータを返す)
Python

同じオブジェクトを何度でもforで回したいなら、iterは“毎回新しいイテレータ”を返すのが安全です。


ジェネレータで簡潔に書く(nextを自分で実装しない近道)

yieldを使った最短の反復定義

class Lines:
    def __init__(self):
        self._buf: list[str] = []

    def add(self, s: str):
        if "\n" in s:
            raise ValueError("1行に改行不可")
        self._buf.append(s)

    def __iter__(self):
        for s in self._buf:
            yield s  # ジェネレータ:__next__とStopIterationは自動処理
Python

yieldを使えば、nextやStopIterationを明示せず、自然な反復が書けます。最初に使うならジェネレータが一番わかりやすいです。


設計の要点(終了条件・副作用・再利用性)

終了条件を明確にする

“尽きたらStopIteration”が反復の契約です。値がなくなった瞬間に迷わずraise StopIterationして、呼び手(forやnext)が自然に終了できるようにします。

副作用は小さく、予測可能にする

nextの中で重いI/Oや不規則な副作用を入れると、1ステップのコストが読めなくなります。ログや軽い検証は許容範囲ですが、接続や書き込みは明示メソッドに分離する方が安全です。

再利用性を意識する

“自分自身がイテレータ”の設計は、一度回し切ると空になります。何度も回したい用途なら、毎回新しいイテレータを返すか、ジェネレータで定義するのが適します。


実務での使いどころ(ページング・ストリーム・変換)

ページングAPIを順次要素にする

import requests

class Users:
    def __init__(self, base_url: str):
        self.base_url = base_url

    def __iter__(self):
        page = 1
        while True:
            r = requests.get(f"{self.base_url}/users", params={"page": page}, timeout=5.0)
            r.raise_for_status()
            data = r.json().get("items", [])
            if not data:
                break
            for u in data:
                yield {"id": str(u.get("id", "")), "name": u.get("name") or "unknown"}
            page += 1
Python

巨大データでも“逐次返す”設計にすると、メモリ効率と見通しが良くなります。

ストリーム変換を段階的につなぐ

class NormalizedUsers:
    def __init__(self, source):
        self.source = source  # イテラブル(Usersなど)

    def __iter__(self):
        for u in self.source:
            yield {"id": u["id"], "name": u["name"].strip()}
Python

イテラブルからイテラブルへ、段階的に変換する構成は、安全で拡張しやすいです。


よくある落とし穴(StopIterationの誤用・例外の握りつぶし・無限反復)

StopIterationを忘れて無限ループ化する

終了条件を満たしたら必ずStopIterationを送出してください。returnではなくraise StopIterationが契約です(ジェネレータはreturnで自動的に終了します)。

例外を握りつぶして欠損データにする

next内のエラーを無視すると、原因不明の欠損になります。必要ならドメイン例外へラップして明確に伝えるか、ログを残して止める設計にします。

無限反復の管理を怠る

“終わらない”イテレータも実用的ですが、呼び手側でbreak条件や件数制限を必ず設けましょう。テストでは上限を付けて検証します。


例題(ファイルの非空行を逐次返す安全なイテラブル)

逐次処理でメモリを守り、終了も確実に

class FileLines:
    def __init__(self, path: str, encoding: str = "utf-8"):
        self.path = path
        self.encoding = encoding

    def __iter__(self):
        with open(self.path, "r", encoding=self.encoding) as f:
            for line in f:
                s = line.rstrip("\n")
                if not s:
                    continue
                yield s
Python

巨大ファイルでも一行ずつ処理でき、withに閉じ込めることで資源のクリーンアップも自動化できます。


まとめ(nextは反復の心臓。終了はStopIteration、実装はジェネレータが近道)

nextは“次の要素”を返し、尽きたらStopIterationで終える反復の心臓です。イテラブルはiterでイテレータを渡し、イテレータはiternextを持つ。最短で実用的なのは、iterでyieldするジェネレータ設計。副作用を小さく、終了条件を明確に、再利用性に配慮すれば、初心者でも“安全で効率的”な反復処理を自在に構築できます。

タイトルとURLをコピーしました