[Python] 08. ジェネレータ(Generator)


Study / Python    作成日付 : 2020/06/04 18:46:08   修正日付 : 2020/06/04 18:46:08

こんにちは。明月です。


この投稿はPythonのジェネレータ(Generator)に関する説明です。


ジェネレータ(Generator)という辞典的な意味は生成するという意味です。Pythonでもジェネレータ(Generator)は生成する意味ではないことではないですが、繰り返し(ループ)文法でイテレータ(iterator)パターンを実装するような機能です。

少し説明が難しくなりますが、関数を呼び出すたびに結果が違うような機能です。

# 関数生成
def example(first, last, step):
  # numberの数を正義する。
  number = first
  # last値まで繰り返す。
  while number < last:
    # 現在のnumber値を返却する。
    yield number
    # number値をstep値ほど加算する。
    number += step
# example関数を呼び出す。
data = example(1,10,1)
# コンソール出力 - dataはジェネレータタイプ
print(data)
# dataは1から9まで1ずつ増加するジェネレータ
# for를 사용해서 루프를 돌리면 10까지 데이터가 돌아가고 현재의 값이 i가 나온다.
for i in data:
  # コンソール出力
  print(i)
# 改行
print()
# これはrangeの値を出力した。
for i in range(1,10,1)
  # コンソール出力
  print(i)


上の例はジェネレータでrange関数と同じ結果が出るように実装しました。range関数も1から9まで出力して1ずつ増加します。


ジェネレータは上の例みたいに関数で使えますが、実はコンプリヘンションでよく使います。

# range関数を利用して1から9までのリストを生成する。
list1 = list(range(1,10))
# list1の値で偶数の値をリストに作成する。
listret = [x for x in list1 if x%2 == 0]
# list1の値で数の値をジェネレータに作成する。
generator = (x for x in list1 if x%2 == 0)
# 偶数リストを出力する。
for i in listret:
  # コンソール出力
  print(i)
# 改行
print()
# 偶数ジェネレータを出力する。
for i in generator:
  # コンソール出力
  print(i)


参考 - [Python] 05. コンプリヘンション(Comprehension)を使用する方法

上の例をみれば同じ結果になります。コンプリヘンションを利用してリストを作ることとジェネレータを作ることは差がないです。

そうすればジェネレータは別に意味がなさそうです。


でも、パフォーマンスに大幅差があります。

import sys;	
# 確実な差異を確認するため、一万まで登録した。
list1 = list(range(1,10000))
# list1の値で偶数の値をリストに作成する。
listret = [x for x in list1 if x%2 == 0]
# list1の値で数の値をジェネレータに作成する。
generator = (x for x in list1 if x%2 == 0)

# データサイズを出力する。
print(sys.getsizeof(listret));	
print(sys.getsizeof(generator));


メモリをみればリストは43,000バイトを使うしジェネレータは120バイトです。

なんでこの差が出るでしょう。

リストの場合はコンプリヘンションで計算した結果をすべて持っていることです。でも、ジェネレータの場合はデータをメモリに持っていることではなく、構造的なオブジェクトでデータを呼び出す時だけ返却するのでこの結果になりました。(構造的なオブジェクトとは値を持っていることではなく、関数のスタックを持っていることです。)

なので、一回性のデータならリストにデータを格納することではなく、ジェネレータを使うほうがパフォーマンスを上げることができます。


ジェネレータはリストによる短所もあります。

# range関数を利用して1から9までのリストを生成する。
list1 = list(range(1,10))
# list1の値で数の値をジェネレータに作成する。
generator = (x for x in list1 if x%2 == 0)
# 生成されたデータを出力する。
for i in generator:
  # コンソール出力
  print(i)
# 生成されたデータを出力する。
for i in generator:
  # コンソール出力
  print(i)


再使用ができないことです。なので結果は一回だけ出力されました。


ジェネレータはループだけ使うことではありません。

# range関数を利用して1から9までのリストを生成する。	
list1 = list(range(1,10))
# 生成されたデータを出力する。
generator = (x for x in list1 if x%2 == 0)
# next関数を利用すれば一つずつ取得ができる。
# コンソール出力
print(next(generator))
# コンソール出力
print(next(generator))
# コンソール出力
print(next(generator))
# コンソール出力
print(next(generator))
# 値は総4個があるが、5回目に取得すればどんな結果になるだろう?
print(next(generator))


next関数で2,4,6,8のデータを一つずつ出力しました。8の以降はデータがないので、エラーが発生します。


ここまでPythonのジェネレータ(Generator)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿