ジェネレータ 最初の一歩


## はじめに

for 文って便利ですよね。便利なものであるとして話を進めます。

```loop.py
fruits = ["りんご", "バナナ", "オレンジ"]

for fruit in fruits:
    print(f"果物の名前: {fruit}")
```

for 文によって配列内の要素を一つずつ取り出して扱うことができます。
こんな便利な for 文の対象になるにはイテラブルである必要があります。

## イテラブルとはなにか？

イテラブルとは、繰り返し処理可能なオブジェクトであるということで、具体的にイテラブルなオブジェクトとは `__iter__` メソッドを持つオブジェクトのことです。

### サンプルコード:配列を使わない繰り返し

```あすけんで勧められた食材を表示する君.py
def generator() -> Generator[str]:
    """ジェネレータ"""
    foods = """・ほうれん草(食物繊維・副菜)
    ・キウイ(カルシウム・果物)
    ・チアシード(食物繊維)
    ・ドライアプリコット(鉄・果物)
    ・バナナ(食物繊維・果物)
    ・レバー(鉄)
    ・小松菜(鉄・副菜)
    ・牛乳(カルシウム・乳製品)
    ・胡麻(カルシウム・副菜)"""

    lines = [line.strip() for line in foods.split("\n")]
    yield from lines


def main() -> None:
    """ジェネレータ"""
    foods: Generator[str] = generator()

    print(hasattr(foods,"__iter__"))
    for food in foods:
        print("food:",food)
# food: ・ほうれん草(食物繊維・副菜)
# food: ・キウイ(カルシウム・果物)
# food: ・チアシード(食物繊維)
# food: ・ドライアプリコット(鉄・果物)
# food: ・バナナ(食物繊維・果物)
# food: ・レバー(鉄)
# food: ・小松菜(鉄・副菜)
# food: ・牛乳(カルシウム・乳製品)
# food: ・胡麻(カルシウム・副菜)

```

`__iter__ `を持つオブジェクトは反復処理可能であることがわかりました。

ところで配列ではなく、なにか変な関数を使いましたね。これはジェネレータといいます。
ジェネレータでは通常の関数とは異なり、returnではなくyieldを使用します。

## ジェネレータとはなにか？

ジェネレータは、一度に全ての値を生成するのではなく、必要に応じて値を一つずつ生成する仕組みを持つ関数です。
これには次のような利点があります。

- 配列と違って予め中身を作っておかなくて良いため、メモリを節約できます
    - これはつまり、無限の要素を取り出すことができるということです
- カスタムロジックを簡単に組み込むことができ、動的なデータ生成ができます

### サンプルコード:無限データ生成

```無限に人を作る君.py
from dataclasses import dataclass
import random

@dataclass
class Human:
    """人名"""

    name: str
    age: int

def human_name_generator() -> Generator[Human]:
    """無限に名前を生成するジェネレーター関数"""
    name = [
        "Adams",
        "Anderson",
        "Armstrong",
        "Baker",
        "Bennett",
        "Lewis",
        "Lopez",
        "Martinez",
        "Rogers",
        "Ross",
        "Russell",
        "Adams",
        "Ellis",
        "Ford",
        "Freeman",
        "Grant",
    ]

    while True:
        yield Human(
            name=random.choice(name),
            age=random.randint(0,100)
        )


def inf_name_generator() -> None:
    """無限人名生成"""
    infinit_name: Generator[Human] = human_name_generator()

    while True:
        human = next(infinit_name)
        print(human)

# Human(name='Lopez', age=71)
# Human(name='Russell', age=96)
# Human(name='Adams', age=74)
# ...
```

これは無限に人間を生成するプログラムです。
このようなジェネレータ関数から返される値は `__iter__` のみならず `__next__` メソッドを持ち、`next()`関数で次の値を取り出してくことができます。
このようなオブジェクトのことを`イテレータ`と呼びます。

## 終わりに

ジェネレータとイテラブルなオブジェクトの関係を理解することで、Pythonプログラミングの可能性が大きく広がります。特に、無限データ生成やメモリ効率が必要なアプリケーションではジェネレータが特に重要になるでしょう。
あとフィボナッチ数列をジェネレータで実装している例などを調べてみると面白いと思います。

### 語られなかったこと・わからないこと・調べたいこと

- ジェネレータ式
- `__next__`
- イテレータ型
- コルーチン
- 遅延評価

## Link

- [用語集 — Python 3.13.1 ドキュメント](https://docs.python.org/ja/3.13/glossary.html)
- [組み込み型 — Python 3.13.1 ドキュメント](https://docs.python.org/ja/3.13/library/stdtypes.html)
- [用語集 — Python 3.13.1 ドキュメント#term-iterable](https://docs.python.org/ja/3/glossary.html#term-iterable)
- [3. データモデル — Python 3.11.11 ドキュメント](https://docs.python.org/ja/3.11/reference/datamodel.html#generator-functions)
- [6. 式 (expression) — Python 3.11.11 ドキュメント#generator-expressions](https://docs.python.org/ja/3.11/reference/expressions.html#generator-expressions)