Вложенные менеджеры контекста Python - PullRequest
7 голосов
/ 04 января 2012

В этом вопросе я определил менеджер контекста, который содержит менеджер контекста.Какой самый простой правильный способ выполнить это вложение?В итоге я позвонил self.temporary_file.__enter__() в self.__enter__().Однако в self.__exit__ я почти уверен, что должен вызывать self.temporary_file.__exit__(type_, value, traceback) в блоке finally в случае возникновения исключения.Должен ли я устанавливать параметры type_, value и traceback, если в self.__exit__ что-то идет не так?Я проверил contextlib, но не смог найти никаких утилит, которые могли бы помочь с этим.

Оригинальный код вопроса:

import itertools as it
import tempfile

class WriteOnChangeFile:
    def __init__(self, filename):
        self.filename = filename

    def __enter__(self):
        self.temporary_file = tempfile.TemporaryFile('r+')
        self.f = self.temporary_file.__enter__()
        return self.f

    def __exit__(self, type_, value, traceback):
        try:
            try:
                with open(self.filename, 'r') as real_f:
                    self.f.seek(0)
                    overwrite = any(
                        l != real_l
                        for l, real_l in it.zip_longest(self.f, real_f))
            except IOError:
                overwrite = True
            if overwrite:
                with open(self.filename, 'w') as real_f:
                    self.f.seek(0)
                    for l in self.f:
                        real_f.write(l)
        finally:
            self.temporary_file.__exit__(type_, value, traceback)

1 Ответ

10 голосов
/ 04 января 2012

Простой способ создания контекстных менеджеров - contextlib.contextmanager.Примерно так:

@contextlib.contextmanager
def write_on_change_file(filename):
    with tempfile.TemporaryFile('r+') as temporary_file:
        yield temporary_file
        try:
             ... some saving logic that you had in __exit__ ...

Затем используйте with write_on_change_file(...) as f:.
Тело оператора with будет выполнено «вместо» yield.Оберните сам yield в блок try, если вы хотите перехватывать любые исключения, возникающие в теле.

Временный файл всегда будет правильно закрыт (когда заканчивается его блок with).

...