Pythonのイテレータは、一度取り出し始めると、ゼロの位置などへリセットが出来ないそうで、
イテレータをリセットしたり、コピーを作ったりする方法はありません。:
https://docs.python.org/ja/3/howto/functional.html
そのため、イテレータをリセット・コピー可能にするWrapperクラスResetableGtor
を用意しました。動作は、単純に、イテレータから取り出した要素を、リストに保存していき、リセットして前へ戻す時には、リストから以前の要素のデータを取り出すだけになります。
イテレータをリセット・コピー可能にする試行コード
import random
# sec: main
def main(): # 実行切替用
run__main1()
pass
# イテレータをリセット・コピー可能にする試行
def run__main1():
# sec: prepare
def gen_test_gtor():
i = 0
while True:
yield i, random.random() # テスト用に、(順番の番号, 乱数)
i += 1
gtor_orig = gen_test_gtor()
gtor_reset = ResetableGtor(gtor_orig)
# sec: 始めに一度取得
print("始めに一度取得")
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
# sec: リセットして再取得
print("リセットして再取得")
gtor_reset.reset()
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
# sec: 任意位置へリセット
print("任意位置へリセット(3へ)")
gtor_reset.reset(3)
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
# sec: コピーしたもの
print("コピーしたもの")
gtor_reset2 = gtor_reset.copy()
gtor_reset2.reset()
print(next(gtor_reset2))
print(next(gtor_reset2))
print(next(gtor_reset2))
# sec: コピーしたものをリセットして再取得
print("コピーしたものをリセットして再取得")
gtor_reset2.reset()
print(next(gtor_reset2))
print(next(gtor_reset2))
print(next(gtor_reset2))
# sec: コピー元から引き続き取得
print("コピー元から引き続き取得")
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
# sec: リセット機能を終了し、引き続き取得
print("リセット機能を終了し、引き続き取得")
gtor_reset.end_reset()
print(next(gtor_reset))
print(next(gtor_reset))
print(next(gtor_reset))
# sec: コピーしたものをリセットして再取得(まだリセット可能)
print("コピーしたものをリセットして再取得(まだリセット可能)")
gtor_reset2.reset()
print(next(gtor_reset2))
print(next(gtor_reset2))
print(next(gtor_reset2))
# イテレータをリセット・コピー可能にするクラス、Wrapper
class ResetableGtor:
# HACK: イテレータを試しに取り出すとリセットできない為、Wrapperを作成
# ref: イテレータをリセットしたり、コピーを作ったりする方法はありません。
# https://docs.python.org/ja/3/howto/functional.html
def __init__(self, gtor, taken_list=[], i_next=0):
self.gtor_orig = gtor
self.taken_list = taken_list # 既に取り出したもの
self.i_next = i_next
def __next__(self):
if self.i_next is None: # if: リセット機能終了
return next(self.gtor_orig)
elif self.i_next < len(self.taken_list): # if: iが既に取り出したものの中
item_i = self.taken_list[self.i_next]
else: # if: iが新しいものの中
item_i = next(self.gtor_orig)
self.taken_list.append(item_i)
self.i_next += 1
return item_i
def reset(self, i_next=0):
if i_next <= len(self.taken_list): # if: iが既に取り出したものの中、または、新しい次の1個
self.i_next = i_next
else:
raise IndexError("既に取り出したものの間でのみリセット可" + f"(0~{len(self.taken_list)})")
# リセット機能を終了する、これ以降はリセット不可、内部保持のリストを解放して省メモリ
def end_reset(self):
self.taken_list = None
self.i_next = None
# ジェネレータをコピー、内部保持のリストは共通として省メモリ
def copy(self):
return ResetableGtor(self.gtor_orig, self.taken_list, self.i_next)
# sec: entry
if __name__ == "__main__": main()
当コード実行時のコンソール出力
始めに一度取得
(0, 0.1422823753871918)
(1, 0.7957444612115221)
(2, 0.7467023469338347)
リセットして再取得
(0, 0.1422823753871918)
(1, 0.7957444612115221)
(2, 0.7467023469338347)
(3, 0.4969213126221237)
(4, 0.7025567328572484)
任意位置へリセット(3へ)
(3, 0.4969213126221237)
(4, 0.7025567328572484)
(5, 0.5619421955074381)
コピーしたもの
(0, 0.1422823753871918)
(1, 0.7957444612115221)
(2, 0.7467023469338347)
コピーしたものをリセットして再取得
(0, 0.1422823753871918)
(1, 0.7957444612115221)
(2, 0.7467023469338347)
コピー元から引き続き取得
(6, 0.3426861200020309)
(7, 0.16324882859285683)
(8, 0.4264613547755832)
リセット機能を終了し、引き続き取得
(9, 0.26693377512603544)
(10, 0.027910806234261454)
(11, 0.8334200438449296)
コピーしたものをリセットして再取得(まだリセット可能)
(0, 0.1422823753871918)
(1, 0.7957444612115221)
(2, 0.7467023469338347)
参照
イテレータをリセットしたり、コピーを作ったりする方法はありません。:
https://docs.python.org/ja/3/howto/functional.html
Python で iterator を使うときに考える初期化のタイミング:
https://qiita.com/tfull_tf/items/3f988300cb27d4b404ec
イテラブルやイテレータとは:
https://qiita.com/bkh4149/items/fa7c80e4d7077aa609c1
【Python】イテレータについて:
https://qiita.com/sugimochi_1019/items/aad6d002fa8d49b8bbdf
【Python】イテレータとは:
https://qiita.com/HookasSariSat/items/32f31cebb55db92f708c