8

この質問では、コンテキストマネージャーを含むコンテキストマネージャーを定義しました。この入れ子を実現するための最も簡単な正しい方法は何ですか?self.temporary_file.__enter__()に電話してしまいましたself.__enter__()。ただし、では、例外が発生した場合に備えて、finallyブロックself.__exit__を呼び出す必要があると確信しています。self.temporary_file.__exit__(type_, value, traceback)何か問題が発生した場合、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)
4

2 に答える 2

10

コンテキスト マネージャーを作成する簡単な方法は、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ブロックの終了時)。

于 2012-01-04T00:04:21.123 に答える
4

contextlib.contextmanager関数には最適ですが、コンテキストマネージャーとしてクラスが必要な場合は、次のユーティリティを使用しています:

class ContextManager(metaclass=abc.ABCMeta):
  """Class which can be used as `contextmanager`."""

  def __init__(self):
    self.__cm = None

  @abc.abstractmethod
  @contextlib.contextmanager
  def contextmanager(self):
    raise NotImplementedError('Abstract method')

  def __enter__(self):
    self.__cm = self.contextmanager()
    return self.__cm.__enter__()

  def __exit__(self, exc_type, exc_value, traceback):
    return self.__cm.__exit__(exc_type, exc_value, traceback)

これにより、ジェネレータ構文 from を使用して contextmanager クラスを宣言できます@contextlib.contextmanager__enter__手動でandを呼び出さなくても、 contextmanager をネストすることがより自然になり__exit__ます。例:

class MyClass(ContextManager):

  def __init__(self, filename):
    self._filename = filename

  @contextlib.contextmanager
  def contextmanager(self):
    with tempfile.TemporaryFile() as temp_file:
      yield temp_file
      ...  # Post-processing you previously had in __exit__


with MyClass('filename') as x:
  print(x)

これが標準ライブラリにあればいいのに...

于 2020-04-28T01:46:02.863 に答える