yield はPythonのジェネレータ関数を定義するために使用されるキーワードです。ジェネレータ関数は、イテレータを簡単に作成する方法を提供し、特に大量のデータを扱う際にメモリ効率が良いという利点があります。
通常の関数は、一度にすべての処理を実行し、単一の値を返すか、何も返しません。一方、ジェネレータ関数は yield
を使用することで、値を「生成」し、その状態を一時停止することができます。次に値が要求されたときに、中断した場所から実行を再開し、次の値を生成します。
必要になったときにだけ値を生成するため(遅延評価)、すべてのデータをメモリにロードする必要がありません。これは、特に巨大なデータセットや無限のシーケンスを扱う場合に非常に重要です。
基本的な使い方
Python
def sample_generator():
print("generate #1")
yield 1
print("generate #2")
yield 2
print("generate #3")
yield 3
print("generate 完了")
# ジェネレータオブジェクトを作成
gen = sample_generator()
print("next()呼び出し#1")
print(next(gen))
print("next()呼び出し#2")
print(next(gen))
print("next()呼び出し#3")
print(next(gen))
print("ジェネレータをループで呼び出し")
for value in sample_generator():
print(value)
# next()呼び出し#1
# generate #1
# 1
# next()呼び出し#2
# generate #2
# 2
# next()呼び出し#3
# generate #3
# 3
# ジェネレータをループで呼び出し
# generate #1
# 1
# generate #2
# 2
# generate #3
# 3
# generate 完了
この例では、next(gen) が呼び出されるたびに、simple_generator 関数が実行され、yield ステートメントで一時停止します。forループで回すと、最後のprint(“generate 完了”)が呼び出されます。
無限シーケンス
Python
def infinite_numbers():
num = 0
while True:
yield num
num += 1
# 無限のジェネレータなので、必要な数だけ取り出す
gen = infinite_numbers()
print("最初の5つの無限数列:")
for _ in range(5):
print(next(gen))
print("-" * 30)
# 特定の条件で停止する例
print("偶数のみを生成し、10000より小さいもの:")
even_generator = (n for n in infinite_numbers() if n % 2 == 0) # ジェネレータ内包表記
count = 0
for num in even_generator:
if num >= 10000:
break
print(num)
count += 1
# 最初の5つの無限数列:
# 0
# 1
# 2
# 3
# 4
# ------------------------------
# 偶数のみを生成し、10000より小さいもの:
# 0
# 2
# 4
# ...
# 9994
# 9996
# 9998
この例のinfini_numbers( )のように、無限のジェネレータを使って巨大なシーケンスを生成できるのはジェネレータの大きな利点です。通常の関数では、無限ループに陥ってしまうためこのような処理はできません。
大きなファイルの読み込み
Python
import os
# テスト用の巨大ファイルを準備
file_name = "_large_file_.txt"
if not os.path.exists(file_name):
print(f"{file_name} を作成中...")
with open(file_name, "w", encoding="utf-8") as f:
for i in range(100000):
f.write(f"これは{i}行目のデータです。\n")
print(f"{file_name} の作成が完了しました。")
# ファイルを読み込むジェネレータ
def read_large_file(file_path):
print(f"ファイル {file_path} の読み込みを開始します...")
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
yield line.strip() # 改行コードを除去してyield
line_count = 0
for line in read_large_file(file_name):
if line_count < 5: # 最初の5行だけ表示
print(line)
line_count += 1
if line_count % 10000 == 0:
print(f"{line_count}行目まで処理しました...")
# ファイル _large_file_.txt の読み込みを開始します...
# これは0行目のデータです。
# これは1行目のデータです。
# これは2行目のデータです。
# これは3行目のデータです。
# これは4行目のデータです。
# ...
# 60000行目まで処理しました...
# 70000行目まで処理しました...
# 80000行目まで処理しました...
# 90000行目まで処理しました...
# 100000行目まで処理しました...
この例では、ファイル全体をメモリにロードせず、必要な時に1行ずつ読み込むためにメモリ効率が良く、巨大なファイルに対応することができます。
コメント