Python: закрытие и занятия
Вопрос
Мне нужно зарегистрировать 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)
И чтобы не очистить дважды, просто проверьте метод очистки, если необходима очистка или нет, прежде чем очистить.