はじめに
for 文って便利ですよね。便利なものであるとして話を進めます。
fruits = ["りんご", "バナナ", "オレンジ"]
for fruit in fruits:
print(f"果物の名前: {fruit}")
for 文によって配列内の要素を一つずつ取り出して扱うことができます。
こんな便利な for 文の対象になるにはイテラブルである必要があります。
イテラブルとはなにか?
イテラブルとは、繰り返し処理可能なオブジェクトであるということで、具体的にイテラブルなオブジェクトとは __iter__
メソッドを持つオブジェクトのことです。
サンプルコード:配列を使わない繰り返し
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を使用します。
ジェネレータとはなにか?
ジェネレータは、一度に全ての値を生成するのではなく、必要に応じて値を一つずつ生成する仕組みを持つ関数です。
これには次のような利点があります。
- 配列と違って予め中身を作っておかなくて良いため、メモリを節約できます
- これはつまり、無限の要素を取り出すことができるということです
- カスタムロジックを簡単に組み込むことができ、動的なデータ生成ができます
サンプルコード:無限データ生成
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__
- イテレータ型
- コルーチン
- 遅延評価