Сообщить об ошибке.

Менеджеры контекста многократного использования

Одноразовые менеджеры контекста, а также менеджеры контекста с повторным входом поддерживают многократное использование, но они не будут работать или не будут работать правильно, если конкретный экземпляр менеджера контекста уже использовался в операторе 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("Покидаем внешний контекст")

# Покидаем внутренний контекст
# Обратный вызов: из внутреннего контекста
# Покидаем внешний контекст
# Обратный вызов: из внешнего контекста