【初心者向け】Pythonのデータ型「types.GeneratorType」を分かりやすく解説! ジェネレータのしくみとサンプルコードを紹介


イテレータとの違い

  • 遅延評価: ジェネレータは 遅延評価 されるため、次のような処理に適しています。

    • 無限シーケンスの生成
    • 処理の負荷が高いデータのフィルタリング
    • 2つの大きなデータセットの結合
  • メモリ使用量: ジェネレータは、一度にすべてのデータをメモリに保持する必要がないため、メモリ使用量が少ない 傾向があります。これは、大きなデータセットを扱う場合に特に有用です。

  • データの生成: イテレータは既存のコレクションから順に値を返しますが、ジェネレータは 値を生成 します。つまり、ジェネレータは実行時にデータを計算することができます。

ジェネレータのしくみ

ジェネレータは、yield キーワードを使用して値を生成します。yield キーワードは、ジェネレータ関数内で使用され、次の値をイテレータに返すために使用されます。ジェネレータ関数は、通常の関数の構文と同じように定義できますが、yield キーワードを含む点が異なります。

以下のコードは、フィボナッチ数列を生成するジェネレータ関数を定義します。

def fibonacci(n):
  a, b = 0, 1
  for _ in range(n):
    yield a
    a, b = b, a + b

この関数は、n 番までのフィボナッチ数を生成するジェネレータオブジェクトを返します。ジェネレータオブジェクトをイテレートすると、次の値が順々に返されます。

for num in fibonacci(10):
  print(num)

このコードは次の出力を生成します。

0
1
1
2
3
5
8
13
21
34

ジェネレータの使用例

ジェネレータは、さまざまなタスクに使用できます。以下に、いくつかの例を示します。

  • カスタムイテレータの作成: ジェネレータを使用して、独自のイテレーションロジックを備えたカスタムイテレータを作成することができます。
  • ネットワークデータの処理: ジェネレータを使用して、ネットワークデータのストリームを効率的に処理することができます。
  • 大きなファイルを処理する: ジェネレータを使用して、大きなファイルを一度にすべてのデータをメモリに保持することなく処理することができます。

types.GeneratorType 関数

types.GeneratorType 関数は、オブジェクトがジェネレータかどうかを判断するために使用されます。この関数は、ジェネレータオブジェクトの場合に True を返し、そうでない場合は False を返します。

def is_generator(obj):
  return isinstance(obj, types.GeneratorType)

このコードは、obj がジェネレータかどうかを判断する関数です。



例1:フィボナッチ数列の生成

この例では、yield キーワードを使用してフィボナッチ数列を生成するジェネレータ関数を作成します。

def fibonacci(n):
  a, b = 0, 1
  for _ in range(n):
    yield a
    a, b = b, a + b
for num in fibonacci(10):
  print(num)
0
1
1
2
3
5
8
13
21
34

例2:ファイルの行を反復処理する

この例では、ジェネレータを使用してファイルを 1 行ずつ反復処理する方法を示します。

def read_lines(filename):
  with open(filename, 'r') as f:
    for line in f:
      yield line.strip()

この関数は、filename で指定されたファイルの各行を返すジェネレータオブジェクトを返します。

for line in read_lines('data.txt'):
  print(line)

このコードは、data.txt ファイルの各行を次の形式で出力します。

行 1 の内容
行 2 の内容
...

この例では、ジェネレータを使用して、2 から 10 までの偶数を生成するカスタムイテレータを作成する方法を示します。

def even_numbers(start, end):
  for num in range(start, end + 1):
    if num % 2 == 0:
      yield num

この関数は、start から end までの範囲内の偶数を返すジェネレータオブジェクトを返します。

for num in even_numbers(2, 10):
  print(num)
2
4
6
8
10


ジェネレータの代替方法として、以下のような方法があります。

リスト

最も単純な代替方法は、リストを使用することです。リストは、すべてのデータをメモリに保持するため、ジェネレータよりもメモリ使用量が多くなります。しかし、リストは高速にアクセスでき、ランダムなインデックスでの要素へのアクセスも可能です。

def square_numbers(n):
  return [x * x for x in range(n)]

この関数は、n までの数の平方をリストとして返します。

イテレータ

イテレータは、ジェネレータと似ていますが、いくつかの重要な違いがあります。イテレータは、一度にすべてのデータを生成する必要はなく、ジェネレータよりもメモリ使用量が少ない場合があります。しかし、イテレータは遅延評価されず、すべてのデータを生成する必要があるため、処理負荷が高くなります。

def square_numbers_iter(n):
  for x in range(n):
    yield x * x

この関数は、n までの数の平方を返すイテレータオブジェクトを返します。

関数

場合によっては、ジェネレータの代わりに単純な関数を使用することができます。関数は、データを生成したり、値を返したりすることができますが、ジェネレータと同じように遅延評価されるわけではありません。

def square_number(x):
  return x * x

この関数は、引数の平方を返します。

どの方法を選択すべきか

使用する方法は、状況によって異なります。

  • データを遅延評価する必要がある場合: ジェネレータを使用します。
  • 処理速度が重要である場合: 関数を使用します。
  • ランダムなインデックスでの要素へのアクセスが必要な場合: リストを使用します。
  • メモリ使用量が限られている場合: ジェネレータまたはイテレータを使用します。