2012-01-18 5 views
6

私は、書き込み用のファイルを開くクラスを持っています。del MyClassはオブジェクトを呼び出しません.__ 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. 

私はdel myobjmyobj.close()を呼び出す場合、私はこの問題を得ることはありません一方。では、なぜ__del__()が呼ばれていないのですか?

+1

「オブジェクト」から継承してください。そうしないと、6年前から古いスタイルになっています。 – Marcin

+0

さらにコードを表示する必要があります。どのように 'myobj.close()'が何をしているのかを知らずに、物事をどのように変更するのかを教えてくれるでしょうか? – ugoren

+1

'del'は' __del__'を呼び出さないことに注意してください。参照の1つを削除するだけです。一般的には、コンテキストマネージャプロトコル( 'with'文)を使用して明示的に閉じる方がよいでしょう。 – yak

答えて

8

__del__を使用しますか? __del__とガベージコレクションのあるissuesがあります。

あなたが代わりに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 __()メソッドを持ち、参照サイクルの一部であるオブジェクトは、参照サイクル全体を回収不能にします。そのサイクルからしか届くものではありません» –

+0

OPの例で参照サイクルがどのようにあるのか分かりません。一つのオブジェクトがあり、 'del''dを取得し、' __del __() 'は呼び出されません。 – sudo

+1

また、私の状況では、GCがすぐに終了する必要はなく、最終的にはGCを処理させるのですが、常に問題があります。コンテキストマネージャは真の解決策ではありません。それらは、同じ関数呼び出しの中でオブジェクトを閉じ、コードを使用するたびにコードを1レベルインデントすることを制限します。私がこれらのうち20個を作りたいと思ったら、それを読むことはできません。 – sudo

8

これは、delとは異なります。 __del__は、互いに関連していないので、delと同じ名前を持つことは残念です。現代の用語では、__del__メソッドは、ファイナライザと呼ばれ、デストラクタではなく、という違いがあります。

短い違いは、デストラクタが呼び出されたときに保証するのは簡単だということですが、あなたは__del__が呼び出され、それが呼び出されないことがない可能性がある場合については非常に少数の保証を持っています。これを引き起こす可能性のあるさまざまな状況があります。

レキシカルスコープを使用する場合は、withステートメントを使用します。それ以外の場合はmyobj.close()に直接お電話ください。 delステートメントは、オブジェクトではなく参照のみを削除します。

別の質問に別の回答(link)が見つかったので、これについて詳しく説明します。その質問への受け入れられた答えには重大な間違いが含まれていることは残念です。

編集:コメント者が指摘したように、objectから継承する必要があります。これは問題ありませんが、まだ__del__が呼び出されることはありません(あなたは幸運になるかもしれません)。上記のリンクされた答えを見てください。

2

コードはobjectから継承する必要があります - そうしないと、(特殊な場合を除いて)少なくとも6年間は古くなったと考えられています。

あなたがおよそ__del__ここで読むことができます:あなたはobjectから継承する必要がある理由のhttp://docs.python.org/reference/datamodel.html#object.del

ショートバージョンが__del__は、新しいスタイルのクラスで唯一の「魔法」であるということです。

ファイナライザの呼び出しに依存する必要がある場合は、ポータブルで堅牢なソリューションであるため、他の回答で推奨されているコンテキストマネージャのアプローチを使用することを強くお勧めします。

+1

ああ、ちょうどこの "魔法"が動作しないインスタンスを見つけました...代わりにipythonを使用しています。 – tdc

+0

@tdc:私が間違っていない限り、ipythonはcpythonに基づいています。いずれにしても、特定のインスタンスの動作に依存する場合、コードは移植できません。他の回答で推奨されているコンテキストマネージャのアプローチを使用する方がよいでしょう。 – Marcin

0

おそらく、それ以外のものが参照している可能性があります。そのため、__del__はまだ呼び出されていません。我々はそれらを参照するリストを削除するまで、それは__del__NiceClassのインスタンスを呼び出すことはありません

#!/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) 

は、このコードを考えてみましょう。

C++とは異なり、__del__はオンデマンドでオブジェクトを破棄するために無条件に呼び出されていません。 GCは物事を少し難しくします。ここにいくつかの情報があります:http://arctrix.com/nas/python/gc/

+0

このケースでは1つの参照しかありませんでした – tdc

関連する問題