Python : yield を使う関数をジェネレーター関数、内包表記を使うものをジェネレーター式という

TwitterFacebookHatena
  • 公開:2021-9-13
  • 更新:2024-11-12
  • 文章量:1664
  • Python

TL;DR

英語の generate は「生成する、生む」という意味がある。Python におけるジェネレーターはイテレータ(for 文で繰り返せる)の一つである。

ジェネレーターを利用すると、関数の途中で処理を中断し値を返すことができる。これを yield と呼ぶ。その後は処理を再開することができる。

ジェネレーターの作成方法

ジェネレーターの作成方法は以下の 2 つある。

  • ジェネレーター関数
  • ジェネレーター式

内部で yield を使う関数をジェネレーター関数、内包表記を使うものをジェネレーター式という。

ジェネレーター関数のポイント

  • yield 式を使う関数
  • for 文で利用できるイテラブル
  • リスト内の要素数が増えてもメモリ使用量を節約できる
  • 呼び出されるたびに処理を記憶する
  • next 関数で次の値を取り出せる
  • next 関数が呼ばれるたびに、関数の中の処理が次の yield まで進む

ジェネレータ (generator) — Python 3.9.4 ドキュメント

yield 文を含む関数はジェネレーターと呼ばれ、通常の関数とは違った動作をする。関数は return を利用し値を一つを返すが、return を yield に変えることで複数の値を一つずつ作り出すジェネレーターになる。

ジェネレーターを使えば、関数の途中まで処理を進めフリーズする。そして任意のタイミングで処理を再開することができる。以下のコードは、next が呼ばれるまで処理が中断されている例である。値がなくなると例外 StopIteration が返る。

def g():
    yield 1
    yield 2
    yield 3

a = g()
print(next(a))
print(next(a))
print(next(a))

# 1 → 一度中断
# 2 → 一度中断
# 3 → 一度中断
# StopIteration → 値がなくなると例外

以下の例だと next() で呼び出す度に、 100 加算されて終了している。再度、next()を呼び出すと、前の処理が記憶されており、次の処理が再開されて停止している。

def g(n):
     while n:
        print(n + 100)
        yield n
        n -= 1

a = g(5)
next(a)
next(a)

# 105
# 104
# 4

for

for 文でも利用可能である。

def g(n):
     while n:
        yield n
        n -= 1

for i in g(3):
    print(i)

# 3
# 2
# 1

ジェネレーター式

ジェネレーター式を用いて、内包表記を使いイテラブルからジェネレーターを作ることもできる。() で囲む。

これはリスト内包表記。全て計算された。

a = [1, 2, 3, 4, 5]
x = [i+5 for i in a]
x

# [6, 7, 8, 9, 10]

[] を () に変えるとジェネレーター式になる。ジェネレーター式は、計算されない。待機している。

a = [1, 2, 3, 4, 5]
x = (i+5 for i in a)
x

# <generator object <genexpr> at 0x7fdb069b5ed0>

next()を呼び出すと、一つずつ計算される。

a = [1, 2, 3, 4, 5]
x = (i+5 for i in a)
x

print(next(x)) # 6
print(next(x)) # 6 7

リストにすると全て計算される

a = [1, 2, 3, 4, 5]
x = (i+5 for i in a)
x

list(x)

# [6, 7, 8, 9, 10]

Python : yield を使う関数をジェネレーター関数、内包表記を使うものをジェネレーター式という