Одноразовые менеджеры контекста, а также менеджеры контекста с повторным входом поддерживают многократное использование, но они не будут работать или не будут работать правильно, если конкретный экземпляр менеджера контекста уже использовался в операторе with
.
Примером многократно используемого менеджера контекста, но не менеджера с повторным входом (т. к. они также могут использоваться повторно), является класс contextlib.ExitStack()
. Этот класс вызывает все зарегистрированные в настоящее время обратные вызовы, оставляя оператор with
, независимо от того, где были добавлены эти обратные вызовы:
from contextlib import ExitStack stack = ExitStack() with stack: stack.callback(print, "Обратный вызов: из первого контекста") print("Покидаем первый контекст") # Покидаем первый контекст # Обратный вызов: из первого контекста with stack: stack.callback(print, "Обратный вызов: из второго контекста") print("Покидаем второй контекст") # Покидаем второй контекст # Обратный вызов: из второго контекста # пробуем использовать как менеджер # контекста c повторным входом with stack: stack.callback(print, "Обратный вызов: из внешнего контекста") with stack: stack.callback(print, "Обратный вызов: из внутреннего контекста") print("Покидаем внутренний контекст") print("Покидаем внешний контекст") # Покидаем внутренний контекст # Обратный вызов: из внутреннего контекста # Обратный вызов: из внешнего контекста # Покидаем внешний контекст
Как видно из выходных данных примера, повторное использование одного объекта stack
в нескольких операторах работает правильно, но попытка вложения их приведет к очистке stack
в конце самого внутреннего оператора with
, что вряд ли будет желательным поведением.
Использование отдельных экземпляров ExitStack
вместо повторного использования одного экземпляра позволяет избежать этой проблемы:
from contextlib import ExitStack with ExitStack() as outer_stack: outer_stack.callback(print, "Обратный вызов: из внешнего контекста") with ExitStack() as inner_stack: inner_stack.callback(print, "Обратный вызов: из внутреннего контекста") print("Покидаем внутренний контекст") print("Покидаем внешний контекст") # Покидаем внутренний контекст # Обратный вызов: из внутреннего контекста # Покидаем внешний контекст # Обратный вызов: из внешнего контекста