2017-03-16 1 views
0

ジェネレーターと一緒に遊んでいるうちに、興味深いものが見つかりました。私がyieldキーワードで関数を定義し、そこからジェネレータを受け取ったとき、関数を与えられたシーケンスで変数を削除しました。そして* POOF!* - ジェネレータは空になります。ここに私のステップがあります:ジェネレーターからの奇妙な振る舞い

>>> def foo(list_): 
...  for i in list_: 
...    yield i*2 
... 
>>> val = [1, 2, 3, 4] 
>>> gen = foo(val) 
>>> print(tuple(gen)) 
(2, 4, 6, 8) 
>>> del val 
>>> print(tuple(gen)) 
() 
>>> 

不変ではありませんか?あるいは、実際に変数としての値をすべてのオブジェクトに渡すオブジェクトとして機能している場合は、出力を与え、リンクされたシーケンスがないために例外をスローしなかったのはなぜですか?実際には、この例は空のシーケンスを繰り返し処理するかのように説明することができ、その結果、ブロックfor _ in []:は起動しません。

>>> def foo(list_): 
...  for i in list_: 
...    yield i*2 
... 
>>> val = [1, 2, 3, 4] 
>>> gen = foo(val) 
>>> print(tuple(gen)) 
(2, 4, 6, 8) 
>>> del foo 
>>> print(tuple(gen)) 
() 
>>> 

は発電機がここdict.get()機能に似て行動しています:このが例外をスローしませんしかし、なぜ、私が説明カント?私はこれを理解していない。

+0

あなたは 'デルval'をしたという事実を:gen自体はまだそれが完了するまでに必要なものを参照するため、そうgen = foo(val); del foo, valが、それはまだ、既存のいずれかfooまたはval参照せずに値を生成することができ、genオブジェクトを中断されませんあなたが観察したジェネレータの動作とは何の関係もありません。あなたが 'del val'をしなかったなら、あなたは同じ結果を見たでしょう。 – user2357112

答えて

4

これは、削除されるオブジェクトとは関係ありません。代わりに、あなたは発電機を使い果たしました。ジェネレータは1回だけ反復することができます。 2回目の繰り返しが必要な場合は、ジェネレータ関数から新しいジェネレータを作成します。あなたは、参照を削除せずに同じ動作を参照してください。

>>> def foo(list_): 
...  for i in list_: 
...   yield i*2 
... 
>>> val = [1, 2, 3, 4] 
>>> gen = foo(val) 
>>> list(gen) 
[2, 4, 6, 8] 
>>> list(gen) # gen is now exhausted, so no more elements are produced 
[] 
>>> gen = foo(val) # new generator 
>>> list(gen) 
[2, 4, 6, 8] 

del fooまたはdel valは唯一のオブジェクトへの参照を削除していること。関数から作成された既存のジェネレータからのように、他の参照がある場合は、関数またはリストオブジェクトを完全に削除しません。

>>> gen = foo(val) 
>>> del foo, val 
>>> list(gen) # gen still references the list and code objects 
[2, 4, 6, 8] 
+0

しかし、関数とシーケンスの両方を削除した後、私のジェネレータで 'itertools.tee()'を使うとどうなりますか? – MaxLunar

+0

@MaxLunar: 'tee()'も参照を使います。 Pythonのすべてがそうです。 'del'は名前を削除するだけで、参照カウントを減らします。カウントが0になったときだけ、実際に削除されたオブジェクトです。 –

+0

@MaxLunar:言い換えれば、teed-offジェネレータオブジェクトを使用して、生成されたアイテムを引き続き取得できます。 –