题
我需要注册 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)
为了不清理两次,只需在清理之前检查清理方法,如果需要清理。