ジェネレータ式は、リスト内包表記に似た構文で簡潔にジェネレータを作成できる方法です。括弧の種類が異なるだけで、メモリ効率に大きな違いがあります。
基本構文
リスト内包表記の角括弧 [] を丸括弧 () に変えるだけで、ジェネレータ式になります。
# リスト内包表記(すべてをメモリに展開) squares_list = [x ** 2 for x in range(5)] # ジェネレータ式(遅延評価) squares_gen = (x ** 2 for x in range(5)) print(type(squares_list)) # <class 'list'> print(type(squares_gen)) # <class 'generator'>
リスト内包表記との違い
リスト内包表記 [...]
すべての要素を一度に生成してメモリに保持する。要素に何度でもアクセスできる。
ジェネレータ式 (...)
要素を1つずつ遅延生成する。一度しかイテレートできない。メモリ効率が良い。
使い方
ジェネレータ式は for 文や next() で値を取り出します。
gen = (x * 10 for x in range(3)) print(next(gen)) # 0 print(next(gen)) # 10 print(next(gen)) # 20
リストに変換することもできます。
gen = (x ** 2 for x in range(5)) result = list(gen) print(result) # [0, 1, 4, 9, 16]
関数の引数として使う
ジェネレータ式を関数の引数に直接渡す場合、外側の括弧を省略できます。
# sum() の引数にジェネレータ式を渡す total = sum(x ** 2 for x in range(10)) print(total) # 285 # 括弧を省略しない書き方 total = sum((x ** 2 for x in range(10)))
sum(), max(), min(), any(), all() などと組み合わせると便利です。
条件付きジェネレータ式
if を使ってフィルタリングできます。
# 偶数の二乗だけを生成 even_squares = (x ** 2 for x in range(10) if x % 2 == 0) print(list(even_squares)) # [0, 4, 16, 36, 64]
メモリ効率の比較
大量のデータを扱う場合、ジェネレータ式は圧倒的にメモリ効率が良いです。
import sys # 100万要素のリスト list_comp = [x for x in range(1_000_000)] print(sys.getsizeof(list_comp)) # 約 8.5MB # ジェネレータ式 gen_exp = (x for x in range(1_000_000)) print(sys.getsizeof(gen_exp)) # 約 200バイト
ジェネレータ式自体はデータを保持せず、計算方法だけを覚えているため、サイズがほぼ一定です。一度しかイテレートしない処理には、ジェネレータ式を使うことをおすすめします。