質問
登録する必要があります 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
, 、自己拘束の価値は正しくありますか?Atexit()ルーチンを登録するにはどうすればよいですか?
これを行うためのより良い方法はありますか?
編集: :これはPython 2.6.5です
解決
レジストリをグローバルレジストリとその中の関数を呼び出す関数にし、必要に応じてそこからそれらを削除します。
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)
他のヒント
self
do_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を指定しなかったことに注意してください。まあ、それが他の誰かを助ける場合に備えて、とにかくここに答えを残します。
Shankedが彼の投稿を削除して以来、私は有利に話します __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
これは、次のケースをサポートしています。
- オブジェクト。エンドは定期的に呼ばれます。すぐにクリーンアップ
- なしでリリースされるオブジェクト。最後の参照がなくなるとクリーンアップ
- サイクルに住んでいるオブジェクト。 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()
クリーンアップオブジェクトにFooに戻る参照がないことが重要です。
コードは大丈夫だと思います。登録解除する方法はありませんが、クリーンアップを無効にするブールフラグを設定できます。
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
最後に、それを念頭に置いてください atexit
ハンドラーは電話と呼ばれません」このプログラムは、Pythonによって処理されない信号、Pythonの致命的な内部エラーが検出された場合、またはOS._Exit()が呼び出されたときに殺されます。"
やってみませんか?チェックするのに1分しかかかりませんでした。
(回答:はい)
ただし、簡素化できます。閉鎖は必要ありません。
class Foo:
def cleanup(self):
pass
def start(self):
atexit.register(self.cleanup)
また、2回クリーンアップしないようにするには、クリーンアップが必要かどうかを確認する前にクリーンアップ方法を確認してください。