6

atexitクラスで使用する関数を登録する必要があります (Foo例については以下を参照)。残念ながら、メソッド呼び出しを介してクリーンアップする直接的な方法はありません: 私が制御できない他のコード、呼び出しFoo.start()Foo.end()しかしエラーが発生した場合に呼び出されFoo.end()ないことがあるので、自分でクリーンアップする必要があります。

このコンテキストでのクロージャーに関するアドバイスを使用できます。

class Foo:
  def cleanup(self):
     # do something here
  def start(self):
     def do_cleanup():
        self.cleanup()
     atexit.register(do_cleanup)
  def end(self):
     # cleanup is no longer necessary... how do we unregister?
  • クロージャーは適切に機能しますか? たとえば、 でdo_cleanup、self の値は正しくバインドされていますか?

  • atexit() ルーチンの登録を解除するにはどうすればよいですか?

  • これを行うより良い方法はありますか?

編集:これはPython 2.6.5です

4

5 に答える 5

6

レジストリをグローバル レジストリとその関数を呼び出す関数にし、必要に応じてそこから削除します。

cleaners = set()

def _call_cleaners():
    for cleaner in list(cleaners):
        cleaner()

atexit.register(_call_cleaners)

class Foo(object):
  def cleanup(self):
     if self.cleaned:
         raise RuntimeError("ALREADY CLEANED")
     self.cleaned = True
  def start(self):
     self.cleaned = False
     cleaners.add(self.cleanup)
  def end(self):
     self.cleanup()
     cleaners.remove(self.cleanup)
于 2010-12-28T16:00:59.667 に答える
3

シャンクが彼の投稿を削除したので、私は__del__再び賛成して話します:

import atexit, weakref
class Handler:
    def __init__(self, obj):
        self.obj = weakref.ref(obj)
    def cleanup(self):
        if self.obj is not None:
            obj = self.obj()
            if obj is not None:
                obj.cleanup()

class Foo:
    def __init__(self):
        self.start()

    def cleanup(self):
        print "cleanup"
        self.cleanup_handler = None

    def start(self):
        self.cleanup_handler = Handler(self)
        atexit.register(self.cleanup_handler.cleanup)

    def end(self):
        if self.cleanup_handler is None:
            return
        self.cleanup_handler.obj = None
        self.cleanup()

    def __del__(self):
        self.end()

a1=Foo()
a1.end()
a1=Foo()
a2=Foo()
del a2
a3=Foo()
a3.m=a3

これは、次の場合をサポートします。

  • .endが定期的に呼び出されるオブジェクト。すぐにクリーンアップ
  • .endが呼び出されずに解放されるオブジェクト。最後の参照がなくなったときのクリーンアップ
  • 周期的に生きる物体; atexitのクリーンアップ
  • 生き続けているオブジェクト。atexitのクリーンアップ

クリーンアップハンドラーがオブジェクトへの弱参照を保持することが重要であることに注意してください。そうしないと、オブジェクトが存続します。

編集:Fooはを実装しているため、Fooを含むサイクルはガベージコレクションされません__del__。ガベージコレクション時にサイクルが削除されるようにするには、クリーンアップをサイクルから削除する必要があります。

class Cleanup:
    cleaned = False
    def cleanup(self):
        if self.cleaned:
            return
        print "cleanup"
        self.cleaned = True
    def __del__(self):
        self.cleanup()

class Foo:
    def __init__(self):...
    def start(self):
        self.cleaner = Cleanup()
        atexit.register(Handler(self).cleanup)
    def cleanup(self):
        self.cleaner.cleanup()
    def end(self):
        self.cleanup()

CleanupオブジェクトにFooへの参照がないことが重要です。

于 2010-12-28T16:22:56.597 に答える
3

コードは問題ないと思います。登録を解除する方法はありませんが、クリーンアップを無効にするブール フラグを設定できます。

    class Foo:
      def __init__(self):
         self.need_cleanup = True
      def cleanup(self):
         # do something here
         print 'clean up'
      def start(self):
         def do_cleanup():
            if self.need_cleanup:
               self.cleanup()
         atexit.register(do_cleanup)
      def end(self):
         # cleanup is no longer necessary... how do we unregister?
         self.need_cleanup = False

最後に、 " Python によって処理されないシグナルによってプログラムが強制終了された場合、Python の致命的な内部エラーが検出された場合、または os._exit() が呼び出されたatexit場合" の場合、ハンドラーは呼び出されないことに注意してください。

于 2010-12-28T16:00:30.423 に答える
3

selfdo_cleanup へのコールバック内で正しくバインドされていますが、実際にメソッドを呼び出しているだけの場合は、バインドされたメソッドを直接使用することもできます。

コールバックを削除するために使用atexit.unregister()しますが、登録した同じ関数を登録解除する必要があり、ネストされた関数を使用したため、その関数への参照を保存する必要があるため、ここに問題があります。バインドされたメソッドを使用するという私の提案に従う場合でも、それへの参照を保存する必要があります。

class Foo:
  def cleanup(self):
     # do something here
  def start(self):
     self._cleanup = self.cleanup # Need to save the bound method for unregister
     atexit.register(self._cleanup)
  def end(self):
     atexit.unregister(self._cleanup)

登録された関数を呼び出さずにコードが終了する可能性があることに注意してください。atexitたとえば、プロセスが Windows で ctrl+break で中止されたり、Linux で SIGABRT で強制終了されたりした場合などです。

また、別の回答が示唆しているように、使用することもできます__del__が、プログラムがアクセスする必要がある他のグローバルが削除されるまで呼び出されない可能性があるため、プログラムの終了中にクリーンアップに問題が生じる可能性があります。

私がこの回答を書いたとき、質問はPython 2.xを指定しなかったことに注意して編集しました。まあ、それが他の誰かを助ける場合に備えて、とにかく答えをここに残します。

于 2010-12-28T16:01:56.977 に答える
1

試してみませんか?チェックするのに1分しかかかりませんでした。

(答え:はい)

ただし、単純化することはできます。閉鎖は必要ありません。

class Foo:
   def cleanup(self):
      pass
   def start(self):
      atexit.register(self.cleanup)

また、2 回クリーンアップしないようにするには、クリーンアップする前に、クリーンアップが必要かどうかをクリーンアップ メソッドで確認してください。

于 2010-12-28T16:03:19.000 に答える