ジェネレーターを「リセット」する方法はありません。ただし、イテレータを「コピー」するために使用できます。itertools.tee
>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]
これには値のキャッシュが含まれるため、両方のイテラブルをほぼ同じ速度で反復する場合にのみ意味があります。(言い換えれば、私がここに持っている方法でそれを使用しないでください!)
もう 1 つの方法は、ジェネレーター関数を渡し、繰り返したいときにいつでも呼び出すことです。
def gen(x):
for i in range(x):
yield i ** 2
def make_two_lists(gen):
return list(gen()), list(gen())
ただし、引数を渡すときにジェネレータ関数に引数をバインドする必要があります。あなたはそれを使うことができますlambda
が、多くの人はlambda
醜いと思います. (私ではありません!YMMV。)
>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
言うまでもなく、ほとんどの場合、リストを作成してコピーする方がよいでしょう。
また、この動作を説明するより一般的な方法として、これを考慮してください。ジェネレーターのポイントは、反復間で何らかの状態を維持しながら、一連の値を生成することです。ここで、単にジェネレーターを反復処理する代わりに、次のようなことをしたい場合があります。
z = zip(a, b)
while some_condition():
fst = next(z, None)
snd = next(z, None)
do_some_things(fst, snd)
if fst is None and snd is None:
do_some_other_things()
このループが尽きるかもしれないし、尽きないかもしれないとしましょうz
。これで、不確定な状態のジェネレーターができました! したがって、この時点で、ジェネレーターの動作が明確に定義された方法で制限されていることが重要です。ジェネレーターが出力のどこにあるかはわかりませんが、a) 後続のすべてのアクセスがシリーズの後の値を生成し、b) 一度「空」になると、シリーズのすべての項目を正確に取得したことはわかっています。一度。の状態を操作する能力があればz
あるほど、それを推論するのが難しくなるため、これら 2 つの約束を破る状況は避けるのが最善です。
もちろん、Joel Cornett が以下で指摘しているように、メソッドを介してメッセージを受け取るジェネレーターを作成することは可能です。send
を使用してリセットできるジェネレーターを作成することも可能send
です。ただし、その場合、できることはメッセージを送信することだけであることに注意してください。ジェネレーターの状態を直接操作することはできないため、ジェネレーターの状態に対するすべての変更は (ジェネレーター自体によって、正しく記述されていると仮定して) 明確に定義されています。send
本当にコルーチンを実装するためのものなので、この目的には使用しません。毎日のジェネレーターは、送信された値で何もすることはほとんどありません-私が上で示したまさにその理由によると思います。