Pythonは非常に優れたプログラミング言語ですが,処理速度が遅いというデメリットがあります
あなたもPythonで容量の大きいリストを使った際に,処理が遅いなと感じたことはないでしょうか?
その解決策として,Pythonではジェネレータがあります
本記事では,リストの場合と比較しながらジェネレータの使い方,メリットを簡単に解説し,ジェネレータを複数扱う方法についても解説します
ジェネレータとは?
ジェネレータは関数の一種で,generator iteratorという配列を返し,yield式を含んでいます
一般的な関数との違いは,呼び出しのたびに新たに開始せず,実効状態を記憶するところです
途中で処理が中断した後に再度処理を開始すると,中断したところから再開します
メリット
上記の説明では意味が分からなくても問題ありません
まずはメリットを抑えましょう
- コードが短くなり,可読性が向上する
- メモリ消費が少なく,処理が速くなる
デメリット
ジェネレータはメリットもありますが,デメリットもあります
デメリットがわかれば,次は使い方を見ていきましょう
- 再利用する場合は,変数に置き換える必要がある
ジェネレータを使ってリストの各要素に処理をする
リストの各要素に対して処理を実行し,新しいリストを返すような場合を考えます
ジェネレータなしとありの場合を比較して使い方を解説します
ジェネレータなし
return_list関数は,numsでリストで受け取り,resultというリストを返します
- resultという空リストを作成
- numsの各要素の2乗をresultの要素
def return_list(nums:list):
result = []
for i in nums:
result.append(i*i)
return result
my_nums = return_list([1, 2, 3, 4, 5])
print(my_nums)
# [1, 4, 9, 16, 25]
ジェネレーターあり
ジェネレータでは,returnではなくyieldを用いて値を返します
返値は,generator iteratorで,配列的ななにかになります.
そのため,最終的にlist()やtuple(),set()と任意の配列に直すと使いやすくなります.
またリストを使った場合との差異は,空リストを作成しない,appendがないことです
def return_generator(nums:list):
for i in nums:
yield i*i
my_nums = return_generator([1, 2, 3, 4, 5])
print(list(my_nums))
# [1, 4, 9, 16, 25]
内包表記でジェネレータを生成する
これまで関数での処理を行ってきましたが,内包表記でジェネレータを使い場合について解説します
実はかなり簡単にgeneratorを生成でき,[] → ()とするだけです
list_nums = [x*x for x in [1, 2, 3, 4, 5]]
generator_nums = (x*x for x in [1, 2, 3, 4, 5])
print(list_nums)
print(list(generator_nums))
# [1, 4, 9, 16, 25]
複数のジェネレータを扱うための yield from の使い方
複数のジェネレーターを扱う際に,yield fromを使ってより簡潔にコードを記述できます
最も簡単なyield fromの使い方をまずは紹介します
イテレーターの要素を返すジェネレーター関数です
def foo(nums):
yield from nums
print(list(foo([1, 2, 3, 4, 5])))
# [1, 2, 3, 4, 5]
yield from で他のジェネレーターを扱う
他のジェネレータを扱うこともできます
yield from generator() の形で他のジェネレーターをぶち込みます
def from_foo(nums):
yield from foo(nums)
print(list(from_foo([1, 2, 3, 4, 5])))
# [1, 2, 3, 4, 5]
yield from で複数のジェネレーターを扱う
複数のジェネレーターを扱う場合はyieldを増殖させれば完了です
def poo(nums):
for num in nums:
yield num*num
def from_foo(nums):
yield from foo(nums)
yield from poo(nums)
print(list(from_foo([1, 2, 3, 4, 5])))
# [1, 2, 3, 4, 5, 1, 4, 9, 16, 25]
yield from を使わないと以下のようになります
def from_foo(nums):
for num in nums:
yield num
for num in nums:
yield num*num
print(list(from_foo([1, 2, 3, 4, 5])))
# [1, 2, 3, 4, 5, 1, 4, 9, 16, 25]
参考文献
お疲れ様でした
コメント