python:闭包和类

发布于 2024-10-09 10:44:24 字数 686 浏览 6 评论 0原文

我需要注册一个 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

I need to register an atexit function for use with a class (see Foo below for an example) that, unfortunately, I have no direct way of cleaning up via a method call: other code, that I don't have control over, calls Foo.start() and Foo.end() but sometimes doesn't call Foo.end() if it encounters an error, so I need to clean up myself.

I could use some advice on closures in this context:

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?
  • Will the closure work properly, e.g. in do_cleanup, is the value of self bound correctly?

  • How can I unregister an atexit() routine?

  • Is there a better way to do this?

edit: this is Python 2.6.5

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

眼波传意 2024-10-16 10:44:24

将注册表设置为全局注册表和调用其中函数的函数,并在必要时从其中删除它们。

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)

Make a registry a global registry and a function that calls a function in it, and remove them from there when necessary.

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)
相对绾红妆 2024-10-16 10:44:24

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

    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

最后,请记住,如果“当检测到 Python 致命内部错误或调用 os._exit() 时,程序会被 Python 未处理的信号杀死。”

I think the code is fine. There's no way to unregister, but you can set a boolean flag that would disable cleanup:

    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

Lastly, bear in mind that atexit handlers don't get called if "the program is killed by a signal not handled by Python, when a Python fatal internal error is detected, or when os._exit() is called."

眼眸里的快感 2024-10-16 10:44:24

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。哦,好吧,无论如何我都会把答案留在这里,以防它对其他人有帮助。

self is bound correctly inside the callback to do_cleanup, but in fact if all you are doing is calling the method you might as well use the bound method directly.

You use atexit.unregister() to remove the callback, but there is a catch here as you must unregister the same function that you registered and since you used a nested function that means you have to store a reference to that function. If you follow my suggestion of using a bound method then you still have to save a reference to it:

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)

Note that it is still possible for your code to exit without calling ther atexit registered functions, for example if the process is aborted with ctrl+break on windows or killed with SIGABRT on linux.

Also as another answer suggests you could just use __del__ but that can be problematic for cleanup while a program is exiting as it may not be called until after other globals it needs to access have been deleted.

Edited to note that when I wrote this answer the question didn't specify Python 2.x. Oh well, I'll leave the answer here anyway in case it helps anyone else.

西瓜 2024-10-16 10:44:24

由于 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

这支持以下情况:

  • 定期调用 .end 的对象;立即清理
  • 未调用 .end 而释放的对象;最后一次清理
    引用会消失
  • 循环中的对象; atexit
  • 清理保持活动状态的 对象; cleanup 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()

重要的是 Cleanup 对象没有对 Foo 的引用。

Since shanked deleted his posting, I'll speak in favor of __del__ again:

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

This supports the following cases:

  • objects where .end is called regularly; cleanup right away
  • objects that are released without .end being called; cleanup when the last
    reference goes away
  • objects living in cycles; cleanup atexit
  • objects that are kept alive; cleanup atexit

Notice that it is important that the cleanup handler holds a weak reference
to the object, as it would otherwise keep the object alive.

Edit: Cycles involving Foo will not be garbage-collected, since Foo implements __del__. To allow for the cycle being deleted at garbage collection time, the cleanup must be taken out of the cycle.

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()

It's important that the Cleanup object has no references back to Foo.

最好是你 2024-10-16 10:44:24

你为什么不尝试一下呢?我只花了一分钟就检查过了。

(答案:是的)

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

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

并且为了不清理两次,只需在清理之前检查是否需要清理即可。

Why don't you try it? It only took me a minute to check.

(Answer: Yes)

However, you can simplify it. The closure isn't needed.

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

And to not cleanup twice, just check in the cleanup method if a cleanup is needed or not before you clean up.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文