それでは下記の記事の続きをやっていこう!
👇前回までは、コードがある一定の品質を保っているのかを確認する “pythonコードのテスト” について学習してきました。
今回は、ジェネレータ(generator)とイテレータ(iterator)について学習していきましょう!
この内容は、メモリ消費量をできるだけ抑えたコードで実装する必要がる場合など、開発するものによっては必須のスキルになってきます。
初学者にとってはなかなかとっつきにくい箇所もあるかと思いますが、一つずつ理解して進んでいけば問題ありません、頑張っていこう✊
Contents
ジェネレータ(generator)
ジェネレータとは
✅ メモリを節約する仕組み
✅全てのデータをメモリに置く代わりに、イテレーション時に毎回出力することで最小限のメモリ消費で抑えることができる(HDから取得や計算、もしくは両方)
👇ジェネレータの例をいくつか書いたので参考にしてください
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# メモリの消費量を確認するモジュールの呼び出し import sys # range関数を変数に格納 range_nums = range(19) for i in range_nums: print(i) # リストを変数に格納 list_nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] for i in list_nums: print(i) # ジェネレータであるrangeオブジェクトを使う方がメモリの消費量を抑えることができる print(sys.getsizeof(range_nums)) # 48 print(sys.getsizeof(list_nums)) # 216 # csv.Dictreaderもジェネレータの一つなので、メモリ消費量を確認する import csv with open('example.csv') as f: reader = csv.DictReader(f) print(sys.getsizeof(reader)) # 48 for line in reader: print(line) |
ジェネレータを自作する
✅ return の代わりにyieldを使うと、その関数はgenerator(generator function)になる
✅ generator functionの戻り値はgenerator iteratorと呼ばれるもので、イテレーションによりyieldの値を返すオブジェクトとなる
✅ generatorは状態を保持している(次にyieldに値が入ってくるまで、保持する)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# range(10)を自作する def myrange(stop): # myrange:generator function start = 0 while start < stop: yield start start += 1 # mr: generator iterator mr = myrange(10) # print(type(mr)) # <class 'generator'> print(next(mr)) # 0 print(next(mr)) # 1 print(next(mr)) # 2 print(next(mr)) # 3 print(next(mr)) # 4 print("end of next") print("beginning of iterator") for i in mr: print(i) # 5 6 7 8 9 def even(num): while num != 0: if num % 2 == 0: yield num num -= 1 for i in even(10): print(i) # 10 8 6 4 2 |
イテレータ(iterator)
イテレータ(iterator)とイテラブル(iterable)
✅ generator:iterableの一種
✅ iterator:next()で回すことができて、iter()関数でイテレータ(iterator)を返すものの総称
✅ iter()関数でiteratorを返すものをiterableという(iteratorもiterable)
next()と.__next__()
✅ next(obj)はobj.__next__()と等しい
✅ iter()は__iter__()メソッドをcallしている
✅ next()で値を返すということは、__next__メソッドを実装しているということ
⇨ iteratorは__next__()と__iter__()が正しく実装されたオブジェクト
1 2 3 4 5 6 7 8 |
cars = ["Collora", "Rav4", "CH-R"] print(next(cars)) # TypeError: 'list' object is not an iterator # リストはイテレータではない print(iter(cars)) # <list_iterator object at 0x109a783a0> cars_iterator = iter(cars) print(next(cars_iterator)) # Collora print(next(cars_iterator)) # Rav4 print(next(cars_iterator)) # CH-R # リストはiterableだと言える |
イテレータを自作する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 1ずつインクリメントするイテレータ class MyIterator: def __init__(self, start=0): self.current = start def __next__(self): num = self.current self.current += 1 return num def __iter__(self): return self myiterator = MyIterator(10) print(next(myiterator)) # 10 print(next(myiterator)) # 11 print(next(myiterator)) # 12 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# 偶数をデクリメントするイテレータ class EvenIterator: def __init__(self, start): self.current = start def __next__(self): if self.current < 2: raise StopIteration elif self.current % 2 == 0: num = self.current self.current -= 2 return num else: self.current -= 1 return self.__next__() def __iter__(self): return self if __name__ == "__main__": even = EvenIterator(10) print(next(even)) # 10 print(next(even)) # 8 print(next(even)) # 6 print(next(even)) # 4 print(next(even)) # 2 |
generator expression
✅ yieldを使わないでgeneratorを作る
✅ 書き方はリスト内包表記と同様、括弧[]ではなく()を使う
✅ generator関数よりも簡潔に書ける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
square_list = [num * num for num in range(100)] print(square_list) # [1, 4, 9, 16, ...] import sys # generator expression(リストでメモリが重くなりそうな時に使う) square_gen = (num * num for num in range(100)) print(next(square_gen)) # 0 print(next(square_gen)) # 1 print(next(square_gen)) # 4 # メモリ消費量のチェック print(sys.getsizeof(square_list)) # 904 print(sys.getsizeof(square_gen)) # 112 even_square = [num * num for num in range(10) if num % 2 == 0] print(even_square) # [0, 4, 16, 36, 64] # generator expression even_square_gen = (num * num for num in range(10) if num % 2 == 0) for num in even_square_gen: print(num) # 0 4 16 36 64 |
“ジェネレータ(generator)とイテレータ(iterator)”については以上になります、いかがだったでしょうか?この内容は一度で理解できなくても、繰り返し学習することで少しずつ理解が追いついてくると思います。
次回はデータベースについて学習していきたいと思います、楽しみにしててくださいね🙆♂️
今回はこの辺で、バイバイ👋
コメントを残す