2009-08-04 7 views
3

Pythonでは、オブジェクトがファイナライズされた後に関数を呼び出す方法はありますか?Pythonのweakrefコールバックと__del__の実行順序

weakrefのコールバックはそれを行うと思っていましたが、weakrefのコールバックは、オブジェクトがガベージコレクションされた後、オブジェクト__del__メソッドが呼び出される前に呼び出されます。これはthe notes on weakrefs and garbage collection in the Python trunkに反しているようです。ここに例があります。

import sys 
import weakref 

class Spam(object) : 
    def __init__(self, name) : 
    self.name = name 

    def __del__(self) : 
    sys.stdout.write("Deleting Spam:%s\n" % self.name) 
    sys.stdout.flush() 

def cleaner(reference) : 
    sys.stdout.write("In callback with reference %s\n" % reference) 
    sys.stdout.flush() 

spam = Spam("first") 
wk_spam = weakref.ref(spam, cleaner) 
del spam 

私が手出力は

$ python weakref_test.py 
In callback with reference <weakref at 0xc760a8; dead> 
Deleting Spam:first 

私がやりたいためにいくつかの他の従来の方法はありますか?私は何とか私のコールバックでファイナライズを強制することはできますか?

+0

、私はすべてのデータセットが使用されなくなったときに、ファイルが自動的に閉じないPyTablesアクセスをラップしようとしている、と私は、「残りの開いているファイルを閉じる...」メッセージを回避。 (これは何百ものファイルの読み込みを抽象化するためのライブラリコードです) – AFoglia

答えて

3

"do do you want"は、リソースがコンテキストを離れるときにコードを実行することを意味します(たとえば、「ガベージコレクタを悪用して不正行為をする」のではなく)間違った方向に向かっています。 Pythonは、withステートメントで使用されているその完全なアイディアをcontext-managersと抽象化しました。

from __future__ import with_statement 
import sys 
class Spam(object): 
    def __init__(self, name): 
     self.name = name 

    def __enter__(self): 
     sys.stdout.write("Entering Spam:%s\n" % self.name) 
     sys.stdout.flush() 

    def __exit__(self, type, value, traceback): 
     sys.stdout.write("Lets clean up Spam:%s\n" % self.name) 
     if type is None: 
      sys.stdout.write("Leaving Spam:%s in peace\n" % self.name) 
      return 
     else: 
      sys.stdout.write("Leaving Spam:%s with Exception:%r\n" % (self.name, value)) 


with Spam("first") as spam: 
    pass 

with Spam("2nd") as spam: 
    raise Exception("Oh No!") 

ができます:ここで

Entering Spam:first 
Lets clean up Spam:first 
Leaving Spam:first in peace 
Entering Spam:2nd 
Lets clean up Spam:2nd 
Leaving Spam:2nd with Exception:Exception('Oh No!',) 
Traceback (most recent call last): 
    File "asd.py", line 24, in <module> 
    raise Exception("Oh No!") 
Exception: Oh No! 
+0

+1: '__del__'を混乱させないでください。 'with'ステートメントを使用します。 –

+2

文を使用すると、リソースを短時間しか使用しない場合でも、クライアントが使用できるリソースを作成し、複数の関数間を渡す場合はうまく動作しますが、非常に不器用です。 with文でファイルオブジェクトしか取得できないとします。それは楽しいことではないでしょう。 – AFoglia

+0

また、私は '__del__'の選択肢がありません。これは、ファイルを読み込むために使用される第三者コード(PyTables)です。レイアウトを読むために必要なオブジェクト間の調整が必要なので、これらのオブジェクトをラップしています。 PyTablesがarファイルを読み書きしたかのように考えると、私のコードはarファイルが有効なdebパッケージレイアウトになっているかどうかを確認します。今度は "ar"を "hdf5"に、 "debパッケージ"を "私たちのデータ"に置き換えてください。 (そして、私たちのレイアウトは複数のファイルで構成されています) '__del__'を削除するのは現実的ではありません。 – AFoglia

0

は、別のスレッドにシリアル化されたごみのループを使用するソリューションです。おそらくあなたが得る最も近い解決策でしょう。好奇心旺盛な方のため

import sys 
from threading import Thread 
from Queue import Queue 
import weakref 

alive = weakref.WeakValueDictionary() 
dump = Queue() 

def garbageloop(): 
    while True: 
     f = dump.get() 
     f() 

garbage_thread = Thread(target=garbageloop) 
garbage_thread.daemon = True 
garbage_thread.start() 

class Spam(object) : 
    def __init__(self, name) : 
    self.name = name 
    alive[id(self)] = self 

    def __del__(self) : 
    sys.stdout.write("Deleting Spam:%s\n" % self.name) 
    sys.stdout.flush() 
    dump.put(lambda:cleaner(id(self))) 

def cleaner(address): 
    if address in alive: 
    sys.stdout.write("Object was still alive\n") 
    else: 
    sys.stdout.write("Object is dead\n") 
    sys.stdout.flush() 

spam = Spam("first") 
del spam