Вопрос

Мне нужно зарегистрировать 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 Зарегистрированные функции, например, если процесс прерывается с помощью CTRL+разрыва в Windows или убит Sigabrt на Linux.

Также, как следует из другого ответа, вы можете просто использовать __del__ Но это может быть проблематичным для очистки, в то время как программа выходит, так как ее не могут быть вызваны до тех пор, пока не будут удалены другие глобальные.

Отредактировано, чтобы отметить, что когда я написал этот ответ, вопрос не указал Python 2.x. О, хорошо, я все равно оставлю здесь ответ, если это поможет кому -либо еще.

С тех пор, как хэнк удалил его публикацию, я буду говорить в пользу __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 называется регулярно; Очистка сразу
  • Объекты, которые выпускаются без называются; Очистка, когда последняя ссылка уходит
  • объекты, живущие в циклах; Очистка Atexit
  • объекты, которые поддерживаются; Очистка 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()

Важно, чтобы объект очистки не имел ссылок на 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 Fatal, или при вызове OS._Exit ()."

Почему бы тебе не попробовать? Мне потребовалась всего минута, чтобы проверить.

(Ответ: Да)

Однако вы можете упростить это. Закрытие не нужно.

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

И чтобы не очистить дважды, просто проверьте метод очистки, если необходима очистка или нет, прежде чем очистить.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top