Шаблон, который иногда можно видеть - представляет собой оператор try ... finally
с переменной flag
, указывающей, следует ли выполнять тело в предложении finally
. В своей простейшей форме, она выглядит примерно так:
cleanup_needed = True try: result = perform_operation() if result: cleanup_needed = False finally: if cleanup_needed: cleanup_resources()
Как и в случае любого кода на основе операторов try
, это может вызвать проблемы при разработке и проверке, поскольку код установки result
и код очистки cleanup_resources()
могут в конечном итоге разделяться произвольно длинными разделами кода.
Класс contextlib.ExitStack()
позволяет вместо этого зарегистрировать обратный вызов для выполнения в конце оператора with
, а затем позднее принять решение, что с этим обратным вызовом делать:
from contextlib import ExitStack with ExitStack() as stack: stack.callback(cleanup_resources) result = perform_operation() if result: stack.pop_all()
Код примера позволяет заданному поведению очистки быть явным заранее, вместо того, чтобы прибегать к переменной флага cleanup_needed
.
Если приложение часто использует этот шаблон, то его можно еще больше упростить с помощью небольшого вспомогательного класса:
from contextlib import ExitStack class Callback(ExitStack): def __init__(self, callback, /, *args, **kwds): super(Callback, self).__init__() self.callback(callback, *args, **kwds) def cancel(self): self.pop_all() # использование with Callback(cleanup_resources) as cb: result = perform_operation() if result: cb.cancel()
Если очистка ресурса к тому же объявлена как отдельная функция, то можно использовать форму декоратора метода ExitStack.callback()
, чтобы объявить очистку ресурса заранее:
from contextlib import ExitStack with ExitStack() as stack: @stack.callback def cleanup_resources(): ... result = perform_operation() if result: stack.pop_all()
Благодаря тому, что протокол декоратора работает, объявленная таким образом функция обратного вызова не может принимать никаких параметров. По этому все освобождаемые ресурсы должны быть доступны как переменные для закрытия.