2014-12-23 156 views
3

刪除我想詢問如何刪除與Python中的自我參照的對象。的Python:自參照對象

讓我們覺得一類,這是一個簡單的例子創建時就知道,當它被刪除:

#!/usr/bin/python 
class TTest: 
    def __init__(self): 
    self.sub_func= None 
    print 'Created',self 
    def __del__(self): 
    self.sub_func= None 
    print 'Deleted',self 
    def Print(self): 
    print 'Print',self 

這個類有一個變量self.sub_func這是我們承擔分配的功能。我想使用TTest的實例將自己的函數分配給self.sub_func。請參見下面的情況:

def SubFunc1(t): 
    t.Print() 
def DefineObj1(): 
    t= TTest() 
    t.sub_func= lambda: SubFunc1(t) 
    return t 

t= DefineObj1() 
t.sub_func() 
del t 

結果是:

Created <__main__.TTest instance at 0x7ffbabceee60> 
Print <__main__.TTest instance at 0x7ffbabceee60> 

就是雖然我們執行 「德爾T」 的說法,,T並未被刪除

我猜測原因是t.sub_func是一個自引用對象,所以t的引用計數器在「del t」不會變爲零,因此t不會被垃圾收集器刪除。

爲了解決這個問題,我需要之前 「德爾T」 插入

t.sub_func= None 

;在這個時候,輸出是:

Created <__main__.TTest instance at 0x7fab9ece2e60> 
Print <__main__.TTest instance at 0x7fab9ece2e60> 
Deleted <__main__.TTest instance at 0x7fab9ece2e60> 

但這很奇怪。 t.sub_func爲t的一部分,所以我不想去計較刪除牛逼結算時t.sub_func。

你能告訴我,如果你知道一個好的解決方案嗎?

+0

爲什麼你關心這個?你可以使用[weakref](https://docs.python.org/2/library/weakref.html)嗎? – BrenBarn

+2

Python垃圾收集器可以檢測週期。在多年的python編程中,我從來沒有使用弱引用,而且我很少**需要明確的刪除。你確定你需要嗎?你有沒有記憶問題? – slezica

+0

也許在'del'後面調用'gc.collect()'? –

回答

2

如何確保在參考週期的對象被刪除時,它不再可達?最簡單的解決方案不是定義__del__方法。很少有課程需要採用__del__方法。 Python並不保證何時或即使調用__del__方法。

有幾種方法可以緩解這個問題。

  1. 使用函數而不是包含並檢查弱引用的lambda。每次調用函數時都需要明確檢查對象是否仍然存在。
  2. 爲每個對象創建一個唯一的類,以便我們可以將該函數存儲在類中而不是猴子修補的函數。這可能會讓記憶變得沉重。
  3. 定義一個屬性,該屬性知道如何獲取給定函數並將其轉換爲方法。我個人最喜歡的,因爲它非常接近從類的未綁定方法創建方法的綁定方式。

使用弱引用

import weakref 

class TTest: 
    def __init__(self): 
     self.func = None 
     print 'Created', self 
    def __del__(self): 
     print 'Deleted', self 
    def print_self(self): 
     print 'Print',self 

def print_func(t): 
    t.print_self() 

def create_ttest(): 
    t = TTest() 
    weak_t = weakref.ref(t) 
    def func(): 
     t1 = weak_t() 
     if t1 is None: 
      raise TypeError("TTest object no longer exists") 
     print_func(t1) 
    t.func = func 
    return t 

if __name__ == "__main__": 
    t = create_ttest() 
    t.func() 
    del t 

創建一個獨特的類

class TTest: 
    def __init__(self): 
     print 'Created', self 
    def __del__(self): 
     print 'Deleted', self 
    def print_self(self): 
     print 'Print',self 

def print_func(t): 
    t.print_self() 

def create_ttest(): 
    class SubTTest(TTest): 
     def func(self): 
      print_func(self) 
    SubTTest.func1 = print_func 
    # The above also works. First argument is instantiated as the object the 
    # function was called on. 
    return SubTTest() 

if __name__ == "__main__": 
    t = create_ttest() 
    t.func() 
    t.func1() 
    del t 

使用屬性

import types 

class TTest: 
    def __init__(self, func): 
     self._func = func 
     print 'Created', self 
    def __del__(self): 
     print 'Deleted', self 
    def print_self(self): 
     print 'Print',self 
    @property 
    def func(self): 
     return types.MethodType(self._func, self) 

def print_func(t): 
    t.print_self() 

def create_ttest(): 
    def func(self): 
     print_func(self) 
    t = TTest(func) 
    return t 

if __name__ == "__main__": 
    t = create_ttest() 
    t.func() 
    del t 
+0

謝謝你的三個想法。我最喜歡第一個(使用弱引用),因爲它代碼少,易於理解(接近C/C++的指針)和直觀。我認爲,在我的情況下,由於「func」總是作爲「t.func」執行,這意味着t(和它的弱引用)存在,所以不需要**顯式檢查對象存在**。 – Akihiko

2

the official CPython docs:具有__del__()方法和是一個參考週期的一部分導致整個參考週期是無法收回的,包括對象中的週期不一定但到達僅從它

對象。 Python不會自動收集這樣的循環,因爲一般來說,Python不可能猜測運行__del__()方法的安全命令。如果您知道安全訂單,則可以通過檢查垃圾列表來強制解決問題,並明確打破由於列表中的對象而導致的週期。請注意,這些對象由於處於垃圾列表中而保持活動狀態,因此它們也應該從垃圾中移除。例如,在中斷循環後,執行del gc.garbage[:]清空列表。通常最好避免使用__del__()方法創建包含對象的循環,並且在這種情況下可以檢查垃圾以確認沒有創建這樣的循環。

參見:http://engineering.hearsaysocial.com/2013/06/16/circular-references-in-python/

+0

年,從技術上說,我的問題似乎是一種循環引用。但其實際含義明顯不同。問題部分有一個層次結構:t.sub_func = lambda:SubFunc1(t); SubFunc1(t)是t的子元素。問題是python無法知道這個層次結構。你認爲使用weakref是告訴python的正確方法嗎? – Akihiko

+0

你需要你的'__del__'方法嗎?很少有班級。如果循環中沒有一個對象具有明確的'__del__',那麼Python可以很好地收集它們。只有當至少有一個'__del__'方法存在時,解釋器拒絕收集循環,因爲它不能猜測'__del__'方法本身需要哪些部分。 – Blckknght