3

循環参照を持つ Python クラスのファイナライザーを作成しようとしています。弱い参照のコールバックが最適な方法であることがわかりました。残念ながら、コールバックとして使用するラムダは決して呼び出されないようです。たとえば、次のコードを実行します。

def del_A(name):
    print('An A deleted:' + name)

class A(object):
    def __init__(self, name):
        print('A created')
        self.name = name
        self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))

class B(object):
    def __init__(self):
        print('B created')

if __name__ == '__main__':
    a = A('a1')
    b = B()
    a.other = b
    b.other = a

戻り値:

A created
B created

循環参照を削除すると、ラムダ コールバックが機能します (「An A deleted: a1」が出力されます)。単純な関数呼び出しでラムダを置き換えることも機能しますが、コールバックを呼び出すときではなく、弱参照を初期化するときにパラメーター値が固定されます。

self._wr = weakref.ref(self, del_A(self.name))
...
a = A('a1')
a.name = 'a2'
b = B()
a.other = b
b.other = a

戻り値:

A created
An A deleted:a1
B created

ラムダコールバックが循環参照で機能しない理由は何ですか?

4

3 に答える 3

3

使用するとき

 self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))  

selfコールバックは、 がファイナライズされようとしているときにのみ呼び出されます。

コールバックが呼び出されない理由は、

a = A('a1')
b = B()
a.other = b   # This gives a another attribute; it does not switch `a` away from the original `a`
b.other = a

aファイナライズされません。オリジナルaはまだあります。

コードを次のように変更すると、コールバックが呼び出されます。

a = A('a1')
b = B()
a = b
b = a

使用するとき

self._wr = weakref.ref(self, del_A(self.name))

あなたのコールバックはNoneです。del_A(self.name)関数への参照ではなく、関数呼び出しそのものです。したがって、すぐにdel_A(self.name)出力され (前に実際にファイナライズされます)、値を返します。これが、weakref のデフォルトのコールバックになります。An A deleted:a1a1None

于 2010-02-19T12:40:29.347 に答える
3

弱い参照が存在する場合にコールバックが呼び出されない理由をようやく見つけたと思います。

「弱い参照オブジェクトが参照するオブジェクトの前に死ぬ」場合、弱い参照コールバックは呼び出されません

循環参照が削除されると、コールバックが呼び出される前にクラス A の弱い参照属性が削除されるようです。1 つの解決策は、ファイナライザー (つまり、弱い参照とそのコールバック) をファイナライザーのリストに追加することです。例えば:

def del_A(name):
    print('An A deleted:' + name)

class A(object):
    def __init__(self, name, finalizers):
        print('A created')
        self.name = name
        finalizers.append(weakref.ref(self, lambda wr, n = self.name: del_A(n)))

class B(object):
    def __init__(self):
        print('B created')

def do_work(finalizers):
    a = A('a1', finalizers)
    b = B()
    a.other = b
    b.other = a

if __name__ == '__main__':
    finalizers = []
    do_work(finalizers)

印刷されます:

A created
B created
An A deleted:a1

do_work() が必要であることに注意してください。そうしないと、コールバックが呼び出される前にファイナライザーが削除されます。明らかに、弱参照の巨大なリストを作成しないように、ファイナライザーを適切に管理する必要がありますが、これは別の問題です。

于 2010-03-07T16:48:30.540 に答える
0

循環参照は自動的にクリーンアップされます。__del__メソッド を定義するクラスなど、いくつかの例外があります。

__del__通常、メソッドを定義する必要はありません

于 2010-02-19T11:58:46.107 に答える