この記事では、Pythonのジェネレータの使い方を解説します。
ジェネレータとは、イテレータを簡単に作成するための機能です。リストやタプルなどを使わなくても複数の値を返す関数を定義することができます。
イテレータについては、以下の記事を参考にしてください。
それでは、ジェネレータの使い方を見ていきましょう!
ジェネレータの使い方
ジェネレータは、関数内でyield
文を使って値を返します。
def 関数名():
yield 戻り値
yield
は、同時にいくつでも定義することもできます。
def 関数名():
yield 戻り値1
yield 戻り値2
yield 戻り値3
- ジェネレータ関数内では
return
は使えない yield
文が処理されても関数は終了しない
サンプル
以下のコードでは、yield
を使って複数の値を返して型と中身を確認しています。
def func():
yield 1
yield 'Hello'
yield [1, 2, 3]
result = func()
print(result)
print(list(result))
実行結果
<generator object func at 0x1039403d0>
[1, 'Hello', [1, 2, 3]]
ジェネレータオブジェクト
ジェネレータ関数を実行すると以下のようなジェネレータオブジェクト(ジェネレータイテレータ)が返されます。
<generator object 関数名 at 0x10bef73d0>
ジェネレータオブジェクトは、for
文で各要素にアクセスしたり、
for val in ジェネレータオブジェクト:
print(val)
リストやタプルに変換したりできます。
list(ジェネレータオブジェクト)
サンプル
以下のコードは、渡された文字列を1文字ずつ文字コードにして返します。
def ords(string: str):
for char in string:
# yieldで文字コードを返す
yield ord(char)
for o in ords('abc'):
print(o)
# リストに変換
print(list(ords('abc')))
実行結果
97
98
99
[97, 98, 99]
ジェネレータの仕組み
ジェネレータは、以下のように処理されています。
yield
文で値を返した時に処理を中断し、その場所を記憶する- 新たに値が要求されたら記憶した場所から処理を再開する
これを繰り返すことでジェネレータは複数の値を返しています。値が要求されるたびに演算して要素を返すため処理を分散することができ、メモリ効率が良いとされています。
サンプル
以下の例を見てください。next()
で要素を要求されるたび、関数内のprint()
によって値が出力されているのがわかります。
def generator():
print('start')
print('yield 1')
yield 1
print('yield 2')
yield 2
print('yield 3')
yield 3
print('end')
g = generator()
# next()で次の要素を要求できる
print('next 1')
print(next(g))
print('next 2')
print(next(g))
print('next 3')
print(next(g))
print('next 4')
print(next(g))
実行結果
next 1
start
yield 1
1
next 2
yield 2
2
next 3
yield 3
3
next 4
end
Traceback (most recent call last):
File "main.py", line 22, in <module>
print(next(g))
StopIteration
このように、ジェネレータは値を要求されるたびに演算して要素を返しています。処理が膨大な場合にはジェネレータを使うことでコストを分散させることができます。
ジェネレータオブジェクトは使い回せない
ジェネレータオブジェクトが呼び出された場合、記憶した場所から処理を再開します。例えば、以下のような場合も同様です。
def func():
yield 1
yield 2
yield 3
g = func()
print('1度目のループ')
for v in g:
print(v)
print('2度目のループ')
for v in g:
print(v)
実行結果
1度目のループ
1
2
3
2度目のループ
1度目のループで最後の値まで処理したので、2度目のループでは何も出力されません。何度も参照したい場合は、リストやタプルに変換して使いましょう!
ジェネレータ式
単純なジェネレータならば、さらに簡潔に式として記述することができます。ジェネレータ式は、内包表記と同じ書き方をします。
(戻り値 for 変数名 in イテラブルオブジェクト)
サンプル
先ほどのサンプルで作成したジェネレータ関数をジェネレータ式で書き換えてみます。
元のコード
def ords(string: str):
for char in string:
# yieldで文字コードを返す
yield ord(char)
for o in ords('abc'):
print(o)
ジェネレータ式で書き換えたコード
for n in (ord(char) for char in 'abc'):
print(n)
実行結果
97
98
99
for
文と合わせて2行で実装できてしまいました。このように単純なジェネレータはジェネレータ式を使うことで簡単に実装できます。
まとめ
この記事では、Pythonのジェネレータの使い方を解説しました。
ジェネレータを使う機会は多くないですが、複数の値を返したいけどリストやタプルなどを使いたくない場合は実装を検討してみてはいかがでしょうか?
一度で処理するにはコストがかかり過ぎる場合にもジェネレータを使うことを考慮しましょう!
簡単に今回のコードをおさらいしておきましょう!
# ジェネレータ
def 関数名():
yield 戻り値1
yield 戻り値2
yield 戻り値3
# ジェネレータ式
(戻り値 for 変数名 in イテラブルオブジェクト)
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ