2010-02-19 82 views
3

我正在嘗試爲具有循環引用的Python類編寫終結器。我發現弱參考回調是way to go。不幸的是,似乎我用作回調的lambda從未被調用過。例如,在運行此代碼:由於循環引用不會調用弱引用回調

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 

卸下循環引用使得拉姆達回調作品( '的甲刪除: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 

任何想法,爲什麼lambda回調不適用於循環引用?

回答

2

我想我終於找到了爲什麼回調不弱引用存在所謂的原因:

弱引用回調不叫,如果"weakref object dies before the object it references"

看來,當循環引用刪除,在回調有機會被調用之前,類A的弱引用屬性被刪除。一種解決方案是將終結器(即,弱引用及其回調)附加到終結器列表。例如:

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()是必要的,否則終結被刪除前的回調有一個被稱爲機會。顯然,終結器必須妥善管理,以避免構建一個龐大的弱引用列表,但這是另一個問題。

0

自動清理循環引用。有一些例外,例如定義__del__方法的類。

通常你不需要定義__del__方法

+0

感謝您的回覆,但我相信它不能回答我的問題。我使用弱引用來避免__del__方法。 – Barthelemy 2010-02-25 12:18:20

2

當您使用

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仍然存在。當您使用

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

那麼你的回調是None

回調將被稱爲如果你改變了代碼

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

del_A(self.name)不是對函數的引用,而是函數調用本身。因此,del_A(self.name)立即打印An A deleted:a1(在a1真正完成之前),並返回值爲None,這將成爲weakref的默認回調。

+0

感謝您的回答。我應該澄清我的問題的措辭。我知道a.other = b不會切換a,但我預計在代碼塊結束時,a和b會超出範圍,因此會被垃圾收集/最終化。爲什麼不調用lambda回調?當我刪除循環引用時,lambda回調被調用,這讓我感到困惑。 – Barthelemy 2010-02-19 13:03:46

+0

但是你對self._wr = weakref.ref(self,del_A(self.name))不起作用的解釋是行不通的。 del_A()是一個函數調用,而不是引用,所以它不是傳遞迴調的正確方法。謝謝。 – Barthelemy 2010-03-07 15:46:31