【脱python初心者】リストではなくジェネレータを使う方法

Pythonは非常に優れたプログラミング言語ですが,処理速度が遅いというデメリットがあります

あなたもPythonで容量の大きいリストを使った際に,処理が遅いなと感じたことはないでしょうか?

その解決策として,Pythonではジェネレータがあります

本記事では,リストの場合と比較しながらジェネレータの使い方,メリットを簡単に解説し,ジェネレータを複数扱う方法についても解説します

目次

ジェネレータとは?

ジェネレータは関数の一種で,generator iteratorという配列を返し,yield式を含んでいます

一般的な関数との違いは,呼び出しのたびに新たに開始せず,実効状態を記憶するところです

途中で処理が中断した後に再度処理を開始すると,中断したところから再開します

メリット

上記の説明では意味が分からなくても問題ありません

まずはメリットを抑えましょう

  • コードが短くなり,可読性が向上する
  • メモリ消費が少なく,処理が速くなる

デメリット

ジェネレータはメリットもありますが,デメリットもあります

デメリットがわかれば,次は使い方を見ていきましょう

  • 再利用する場合は,変数に置き換える必要がある

ジェネレータを使ってリストの各要素に処理をする

リストの各要素に対して処理を実行し,新しいリストを返すような場合を考えます

ジェネレータなしとありの場合を比較して使い方を解説します

ジェネレータなし

return_list関数は,numsでリストで受け取り,resultというリストを返します

関数内の処理
  1. resultという空リストを作成
  2. 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]

参考文献

お疲れ様でした

シェアしてくださると嬉しいです!
  • URLをコピーしました!

コメント

コメントする

目次