Типично требовать для некоторой задачи нескольких объектов, у которых есть ресурсы, которые должны быть явно выпущены - скажем, два файла; это легко сделать, когда задача является локальной для функции с использованием вложенных with блоками, или - еще лучше - один with блоком с несколькими with_item пунктов:
with open('in.txt', 'r') as i, open('out.txt', 'w') as o:
    # do stuff
 OTOH, я все еще пытаюсь понять, как это должно работать, когда такие объекты не только локальны для области функций, но принадлежат экземпляру класса - другими словами, как создаются менеджеры контекста.
В идеале я хотел бы сделать что-то вроде:
class Foo:
    def __init__(self, in_file_name, out_file_name):
        self.i = WITH(open(in_file_name, 'r'))
        self.o = WITH(open(out_file_name, 'w'))
  и Foo сам превращается в менеджер контекста, который обрабатывает i и o, так что когда я это делаю
with Foo('in.txt', 'out.txt') as f:
    # do stuff
  self.i и self.o заботятся автоматически, как и следовало ожидать.
Я возился о написании таких вещей, как:
class Foo:
    def __init__(self, in_file_name, out_file_name):
        self.i = open(in_file_name, 'r').__enter__()
        self.o = open(out_file_name, 'w').__enter__()
    def __enter__(self):
        return self
    def __exit__(self, *exc):
        self.i.__exit__(*exc)
        self.o.__exit__(*exc)
  но он как подробный, так и небезопасный в отношении исключений, возникающих в конструкторе. После некоторого времени поиска я нашел этот блог-блог в 2015 году, в котором используется contextlib.ExitStack чтобы получить что-то очень похожее на то, что мне нужно:
class Foo(contextlib.ExitStack):
    def __init__(self, in_file_name, out_file_name):
        super().__init__()
        self.in_file_name = in_file_name
        self.out_file_name = out_file_name
    def __enter__(self):
        super().__enter__()
        self.i = self.enter_context(open(self.in_file_name, 'r')
        self.o = self.enter_context(open(self.out_file_name, 'w')
        return self
 Это довольно удовлетворительно, но я озадачен тем, что:
- Я ничего не знаю об этом использовании в документации, поэтому он, похоже, не является "официальным" способом решения этой проблемы;
 - в общем, мне очень сложно найти информацию об этой проблеме, что заставляет меня думать, что я пытаюсь применить непитологическое решение проблемы.
 
 Некоторый дополнительный контекст: я работаю в основном в C++, где нет разницы между случаем блочной области и случаем объектной области для этой проблемы, так как эта очистка реализована внутри деструктора (подумайте __del__, но вызывается детерминистически), а деструктор (даже если он явно не определен) автоматически вызывает деструкторы подобъектов. Итак, оба:
{
    std::ifstream i("in.txt");
    std::ofstream o("out.txt");
    // do stuff
}
 а также
struct Foo {
    std::ifstream i;
    std::ofstream o;
    Foo(const char *in_file_name, const char *out_file_name) 
        : i(in_file_name), o(out_file_name) {}
}
{
    Foo f("in.txt", "out.txt");
}
 сделайте всю очистку автоматически, как вы обычно этого хотите.
Я ищу аналогичное поведение в Python, но, опять же, я боюсь, что я просто пытаюсь применить шаблон, исходящий из C++, и что основная проблема имеет радикально другое решение, о котором я не могу думать,
 Итак, чтобы подвести итог: что такое решение __enter__ с тем, что объект, которому принадлежат объекты, требующие очистки, становится самим менеджером контекста, правильно вызывая __enter__/__exit__ своих детей?