このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】ジェネレータ式

ジェネレータ式

 ジェネレータ式は、リスト内包表記と非常によく似た構文で値を生成しますが、全ての値を一度にメモリに格納するのではなく、必要になったときにその都度計算して返す点が大きな特徴です。これにより、大量のデータや無限シーケンスでもメモリ効率よく扱うことができます。ここでは、ジェネレータ式の基本構文、リスト内包表記との違い、そして具体的なサンプルプログラムを通してその活用方法を解説します。

プログラムのダウンロード

 「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.ジェネレータ式の基本

1.1. ジェネレータ式の概要

 ジェネレータ式は、リスト内包表記と同様の記法で値を生成しますが、角括弧 [] の代わりに丸括弧 () を用いる点が異なります。基本構文は以下の通りです。

(式 for 変数 in イテラブル)

 この構文は、イテラブルなオブジェクトから値を1個ずつ取り出し、必要なときにその都度式を評価して値を返すジェネレータオブジェクトを生成します。

1.2. リスト内包表記との違い

 内包表記とジェネレータ式は、どちらも同じ構文の一部を共有しますが、生成されるオブジェクトとメモリの使用方法に違いがあります。以下の表はその違いをまとめたものです。

特徴リスト内包表記ジェネレータ式
記述方法[式 for 変数 in イテラブル](式 for 変数 in イテラブル)
メモリ使用量全ての値を一括してリストに格納必要な時に1個ずつ値を生成
無限シーケンスの生成不可能可能

2.ジェネレータ式の応用例

2.1. 基本例:平方数の生成

 ここでは、0から9までの整数の2乗を生成するジェネレータ式を例に挙げます。リスト内包表記との違いを確認するため、まず内包表記での出力、次にジェネレータ式での出力を示します。

# リスト内包表記で平方数を生成(全要素をメモリに格納)
squares_list = [x * x for x in range(10)]
print("List comprehension:", squares_list)

# ジェネレータ式で平方数を生成(必要な時に1個ずつ計算)
squares_gen = (x * x for x in range(10))
print("Generator expression:", squares_gen)

実行結果

List comprehension: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Generator expression: <generator object <genexpr> at 0x000001561B5779F0>

解説

  • リスト内包表記では、0から9までの各整数に対して x*x が計算され、その結果がリストとして全て生成されます。
  • ジェネレータ式では、squares_gen というジェネレータオブジェクトが生成され、実際に値を取り出すまで計算は行われません。出力では <generator object <genexpr> at 0x…> のように表示されます。

2.2. for文で値を取り出す例

 ジェネレータ式で生成した値は、for文などで逐次取り出すことができます。以下の例では、ジェネレータ式から値を1個ずつ取り出して表示します。

# ジェネレータ式から値を1個ずつ取り出して表示
for num in (x * x for x in range(10)):
    print(num, end=' ')
print()  # 改行

実行結果

0 1 4 9 16 25 36 49 64 81 

解説

  • このfor文は、ジェネレータ式 (x * x for x in range(10)) から1個ずつ値を取り出し、順次表示します。
  • 取り出された値は、リスト内包表記で生成したリストと同じ結果、すなわち
    0 1 4 9 16 25 36 49 64 81
    が出力されますが、全ての値が一度にメモリに格納されるわけではありません。

まとめ

 ジェネレータ式は、内包表記と同じ直感的な構文で値を生成しますが、必要なときに1個ずつ値を作るため、メモリ効率に優れています。ここでは、大量のデータを扱いませんでしたが、大量なデータを扱う場合に、差が出てきます。

  • 基本構文は (式 for 変数 in イテラブル) で、リスト内包表記との違いは角括弧と丸括弧の違いにあります。
  • リスト内包表記は全ての要素を一括生成するのに対し、ジェネレータ式は値を取り出すたびに計算を行うため、巨大なデータや無限シーケンスの生成に適しています。

 これらの特性を理解することで、適切なシチュエーションに応じて内包表記やジェネレータ式を使い分け、効率的で読みやすいコードを書くことが可能になります。