22

発電機を使わなくなったら、ガベージコレクションをするべきですよね?次のコードを試しましたが、どの部分が間違っているのかわかりません。

import weakref
import gc

def countdown(n):
    while n:
        yield n
        n-=1

cd = countdown(10)
cdw = weakref.ref(cd)()
print cd.next()
gc.collect()
print cd.next()
gc.collect()
print cdw.next()

最後から2行目で、ガベージコレクターを呼び出しましたが、これ以上呼び出す必要はありませんcdgc権利を解放する必要がありcdます。しかし、を呼び出すとcdw.next()、まだ8を印刷しています。さらにいくつか試してみましたがcdw.next()、StopIterationまで残りすべてを正常に印刷できました。

ジェネレーターとコルーチンがどのように機能するかを理解したかったので、これを試しました。David BeazleyのPyConプレゼンテーション「コルーチンと並行性に関する興味深いコース」のスライド28で、彼はコルーチンが無期限に実行される可能性があるため、コルーチン.close()をシャットダウンするために使用する必要があると述べました。それから彼はガベージコレクターが呼び出すと言った.close()。私の理解では、私たちが.close()自分自身を呼んだら、もう一度gc電話.close()します。すでに閉じられているコルーチンをgc呼び出すことができないという警告を受け取りますか?.close()

ご入力いただきありがとうございます。

4

4 に答える 4

10

Pythonの動的な性質により、cd(少なくとも)PythonのCpython実装は「先読み」しないため、現在のルーチンの最後に到達するまで、への参照は解放されません。(使用しているPython実装がわからない場合は、ほぼ確実に「Cpython」です)。一般的なケースでオブジェクトが現在の名前空間にまだ存在する場合、インタプリタがオブジェクトを解放する必要があるかどうかを判断することを事実上不可能にする微妙な点がいくつかあります(たとえば、への呼び出しによってオブジェクトに到達できますlocals())。

あまり一般的ではない場合、他のPython実装では、現在のスタックフレームが終了する前にオブジェクトを解放できる場合がありますが、Cpythonは気にしません。

代わりに、ジェネレーターがCpythonで自由にクリーンアップできることを示す次のコードを試してください。

import weakref
def countdown(n):
    while n:
        yield n
        n-=1

def func():
    a = countdown(10)
    b = weakref.ref(a)
    print next(a)
    print next(a)
    return b

c = func()
print c()

オブジェクト(ジェネレーターを含む)は、参照カウントが0に達するとガベージコレクションされます(Cpythonの場合-他の実装では動作が異なる場合があります)。Cpythonでは、参照カウントは、delステートメントが表示されたとき、または現在の名前空間が変更されたためにオブジェクトがスコープ外になったときにのみデクリメントされます。

重要なことは、オブジェクトへの参照がなくなると、ガベージコレクターによって自由にクリーンアップできることです。実装がこれ以上参照がないと判断する方法の詳細は、使用している特定のPythonディストリビューションの実装者に残されています。

于 2013-03-19T01:50:56.707 に答える
8

あなたの例では、ジェネレーターはスクリプトが終了するまでガベージコレクションを取得しません。Pythonは、あなたがcd再び使用するかどうかを知らないので、それを捨てることはできません。正確に言うと、グローバル名前空間にはジェネレーターへの参照がまだあります。

ジェネレータは、他のオブジェクトと同様に、参照カウントがゼロになるとGCされます。発電機が使い果たされていなくても。

これは、多くの通常の状況で発生する可能性があります。ローカル名がスコープ外にある場合、del編集されている場合、所有者がGCされている場合です。ただし、ライブオブジェクト(名前空間を含む)がそのオブジェクトへの強力な参照を保持している場合、GCは取得されません。

于 2013-03-19T01:53:14.123 に答える
4

Pythonのガベージコレクターはそれほど賢くはありません。その行以降は参照していませんがcd、参照はローカル変数に存在しているため、収集できません。(実際、使用しているコードの中には、ローカル変数を掘り下げて復活させる可能性があります。可能性は低いですが、可能です。したがって、Pythonは仮定を立てることができません。)

ガベージコレクターに実際にここで何かを実行させたい場合は、次を追加してみてください。

del cd

これにより、ローカル変数が削除され、オブジェクトを収集できるようになります。

于 2013-03-19T01:51:20.423 に答える
1

他の回答ではgc.collect()、まだ参照されているものはガベージコレクションされないことが説明されています。ジェネレーターへのライブ参照がまだ存在するcdため、削除されるまでgcされませんcd

ただし、さらに、OPは、この行を使用してオブジェクトへの2番目の強参照を作成しています。これは弱参照オブジェクトを呼び出します。

cdw = weakref.ref(cd)()

したがって、を実行del cdして呼び出す場合でもgc.collect()、参照であるため、ジェネレータはgcされません。cdw

weakref.ref実際の弱参照を取得するには、オブジェクトを呼び出さないでください。単にこれを行います:

cdw = weakref.ref(cd)

これで、cdが削除されてガベージコレクションが行われると、参照カウントはゼロになり、弱参照を呼び出すと、None期待どおりになります。

于 2017-08-07T04:55:26.387 に答える