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?
- 闭包是否正常工作,例如在
中,self-bound的值是否正确do_cleanup
- 如何注销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)
请注意,您的代码仍然可以在不调用theratexit
注册函数的情况下退出,例如,如果在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的情况下释放的对象;最后一次清理时 参考文献消失了
- 循环生活的物体;反脱欧
- 保持生命的物体;反脱欧
\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中注册我的清理调用–也可以。