2012-01-18 31 views
6

我有一個打開文件進行寫入的類。在我的析構函數,我調用關閉該文件的函數:del MyClass不會調用object .__ del __()

class MyClass: 
    def __del__(self): 
     self.close() 

    def close(self): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

但是當我喜歡的代碼刪除對象:

myobj = MyClass() 
myobj.open() 
del myobj 

,如果我嘗試重新實例化對象,我得到一個數值錯誤:

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

,而如果我叫myobj.close()del myobj之前,我不明白這個問題。那麼爲什麼__del__()沒有被叫?

+1

嘗試從'對象'繼承 - 不這樣做已經被認爲是過時的風格約六年。 – Marcin 2012-01-18 09:37:10

+0

你應該展示更多的代碼。怎麼能告訴你'myobj.close()'如何改變,而不知道它是幹什麼的? – ugoren 2012-01-18 09:37:49

+1

請記住'del'不會調用__del__'。它只是刪除了其中一個參考。通常最好是明確關閉,可能使用上下文管理器協議('with'語句)。 – yak 2012-01-18 09:45:50

回答

8

您確定要使用__del__?有issues__del__和垃圾收集。

你可以做MyClass的一個context manager代替:

class MyClass(object): 
    def __enter__(self): 
     return self 
    def __exit__(self,ext_type,exc_value,traceback): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

通過這樣做,你可以使用MyClass這樣的:

with MyClass() as myobj: 
    ... 

myobj.__exit__(因此self.__fileHandle__.close())將會被調用時Python的葉子with-block

+0

+1提到的問題。 http://docs.python.org/library/gc.html#gc.garbage表示:«具有__del __()方法並且是引用循環的一部分的對象會導致整個引用循環無法收集,包括不一定在該週期,但只能從它到達» – 2012-01-18 10:22:48

+0

我沒有看到在OP的例子中有一個參考週期。有一個對象,它得到'del''d,'__del __()'不會被調用。 – sudo 2017-05-26 17:59:01

+1

另外,在我的情況下,我寧願讓GC處理一些東西,因爲它不需要立即關閉,只是最終......但它總是有問題。不,環境管理者不是真正的解決方案。它們限制你在同一個函數調用中關閉對象,並在每次使用一個級別時將你的代碼縮進一級。當我想創建其中的20個時,使其無法讀取。 – sudo 2017-05-26 18:02:04

8

這不是del所做的。不幸的是,__del__del具有相同的名稱,因爲它們彼此不相關。在現代術語中,__del__方法將被稱爲終結器,而不是析構器並且區別是重要的。

簡短的區別是,它很容易保證當調用析構函數,但你什麼時候__del__將被稱爲極少數的保證,它可能永遠不會叫。有很多不同的情況可能導致這種情況。

如果您想要詞法範圍確定,請使用with語句。否則,請直接撥打myobj.close()。語句del只刪除引用,而不是對象。

我找到了另一個答案(link)到一個不同的問題,更詳細地回答這個問題。不幸的是,對這個問題的接受答案含有極其嚴重的錯誤。

編輯:正如評論者指出,您需要繼承object。這很好,但仍然有可能__del__永遠不會被調用(你可能會很幸運)。請參閱上面的鏈接答案。

2

您的代碼應該從object繼承 - 如果不這樣做,則至少在六年內已被視爲過期(特殊情況除外)。

你可以閱讀__del__這裏:http://docs.python.org/reference/datamodel.html#object.del

爲什麼你需要從object繼承簡短的版本是,__del__僅適用於新的樣式類的「神奇」。

如果您需要依賴調用終結器,我強烈建議您使用其他答案中推薦的上下文管理器方法,因爲這是一種便攜式,強大的解決方案。

+1

剛剛發現一個實例,這個「魔法」不起作用...使用ipython來代替。 – tdc 2012-01-18 10:21:51

+0

@tdc:除非我誤解了,ipython是基於cpython的。無論如何,如果您依賴特定實例的行爲,您的代碼將不可移植。使用其他答案中推薦的上下文管理器方法可能會更好。 – Marcin 2012-01-18 10:46:20

0

也許別的東西引用它,這就是爲什麼__del__沒有被調用(還)。

考慮以下代碼:

#!/usr/bin/env python 

import time 

class NiceClass(): 
    def __init__(self, num): 
     print "Make %i" % num 
     self.num = num 
    def __del__(self): 
     print "Unmake %i" % self.num 

x = NiceClass(1) 
y = NiceClass(2) 
z = NiceClass(3) 
lst = [x, y, z] 
time.sleep(1) 
del x 
del y 
del z 
time.sleep(1) 
print "Deleting the list." 
del lst 
time.sleep(1) 

它不叫NiceClass__del__情況下,直到我們刪除引用它們的名單。

與C++不同,__del__未被無條件地調用來按需破壞對象。 GC讓事情變得更加困難。這裏有一些信息:http://arctrix.com/nas/python/gc/

+0

在這種情況下肯定只有一個參考 – tdc 2012-01-18 10:07:18