python:闭包和类

python:闭包和类,python,class,atexit,Python,Class,Atexit,我需要注册一个atexit函数以用于一个类(请参见下面的Foo示例),不幸的是,我无法通过方法调用直接清理该类:我无法控制的其他代码调用Foo.start()和Foo.end()但有时不调用Foo.end())如果遇到错误,我需要自己清理 在这种情况下,我可以使用一些关于闭包的建议: class Foo: def cleanup(self): # do something here def start(self): def do_cleanup():

我需要注册一个
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-bound的值是否正确

  • 如何注销atexit()例程

  • 有更好的方法吗


编辑:这是Python 2.6.5

我认为代码很好。无法取消注册,但可以设置一个布尔标志来禁用清除:

    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
处理程序

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
在回调中被正确绑定以执行\u清理,但事实上,如果您所做的只是调用该方法,那么您最好直接使用绑定方法

您可以使用
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)
请注意,您的代码仍然可以在不调用ther
atexit
注册函数的情况下退出,例如,如果在windows上使用ctrl+break中止进程,或者在linux上使用SIGABRT终止进程

另一个答案建议您可以只使用
\uuu del\uuu
,但在程序退出时进行清理可能会有问题,因为在删除它需要访问的其他全局文件之前,可能不会调用它


编辑后注意到,当我写这个答案时,这个问题没有指定Python2.x。哦,好吧,我还是把答案留在这里,以防对其他人有帮助。

你为什么不试试呢?我只花了一分钟检查

(答:是的)

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

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

要避免两次清理,只需在清理之前检查清理方法是否需要清理。

既然shanked删除了他的帖子,我将再次支持
\uu del\uu

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的情况下释放的对象;最后一次清理时 参考文献消失了
  • 循环生活的物体;反脱欧
  • 保持生命的物体;反脱欧
请注意,清理处理程序保存弱引用非常重要 对于对象,否则会使对象保持活动状态

编辑:涉及Foo的循环将不会被垃圾收集,因为Foo实现了
\uu del\uu
。为了允许在垃圾收集时删除循环,必须将清理工作从循环中取出

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的引用是很重要的

我希望这是Python3,否则不继承
对象
会给你一个可怕的老式类…老式?[Scratchs head]请详细说明,这样我就可以避免错误销售的样式类是在类和类型统一之前的类,如果你不继承
对象
,你就会得到一个旧样式的类。所以我应该声明
类Foo(object):
?@Glenn:如果你觉得这里有问题,然后发布您自己的改进答案,而不是抛出非建设性的批评。注意atexit.unregister只存在于Python 3中。+1闭包本身是非常无用的,因为您可以直接执行
atexit.register(self.cleanup)
。-1。这将泄漏atexit表中的条目;如果创建了一百万个这样的对象,那么一百万个项目将永久地留在atexit表中。这不好。这还会泄漏对对象本身的引用,这也绝对不好。这不起作用:如果您尝试,您将得到“在迭代期间设置更改的大小”。您只需将for循环更改为
for cleaner in list(cleaners)
,然后就可以了。它还将使对象保持活动状态,以防任何地方都有循环引用涉及它。如果要使用
\uuuu del\uuuu
至少使该处理程序对象具有
\uuu del\uuuu
方法而不是
Foo
,并存储对Foo对象的弱引用,使
处理程序在原始对象被删除时自动删除。@Rosh矛盾修饰法:否,让处理程序实现
\uu del\uu
是不好的。此对象一直保留到退出,因为它是atexit处理程序。您是对的,对象在循环中不会被垃圾收集。要支持这一点,您需要另一个进行清理的对象,请参阅我的编辑。这很好。我选择在我的类init中注册我的清理调用–也可以。