我需要注册 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+断裂中止或在Linux上被Sigabrt杀死。

另外一个答案也暗示您可以使用 __del__ 但这对于清理程序可能会出现问题,因为一个程序正在退出时,直到需要删除其他Globals之后,它才能被调用。

编辑要注意,当我编写此答案时,问题没有指定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

这支持以下情况:

  • 定期调用端的对象;立即清理
  • 释放的对象,无端。最后一个参考消失时清理
  • 生活在周期中的物体;清理充沛
  • 保持活力的物体;清理充沛

请注意,清理处理程序对对象的参考很重要,否则它将使对象保持生命。

编辑: :涉及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()时。"

你为什么不尝试?我只花了一分钟的时间检查。

(答案:是)

但是,您可以简化它。不需要关闭。

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

为了不清理两次,只需在清理之前检查清理方法,如果需要清理。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top