2012-01-03 12 views
7

this questionに、私はコンテキストマネージャを含むコンテキストマネージャを定義しました。この入れ子を達成する最も簡単な正しい方法は何ですか?私はself.__enter__()self.temporary_file.__enter__()を呼んでしまった。しかし、self.__exit__では、例外が発生した場合にfinallyブロックにself.temporary_file.__exit__(type_, value, traceback)を呼び出す必要があると私は確信しています。 self.__exit__で何か問題が生じた場合は、type_、value、およびtracebackパラメータを設定する必要がありますか?私はcontextlibをチェックしましたが、これを助けるユーティリティは見つかりませんでした。コンテキストマネージャを作成する簡単な方法は、contextlib.contextmanagerであるPythonコンテキストマネージャをネストする

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) 

答えて

9

:質問から

オリジナルコード。このようなもの:

@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の代わりに "実行されます。体内で発生した例外をキャッチする場合はyieldtryブロックで囲みます。

テンポラリファイルは常に正しく閉じられます(withブロックが終了した場合)。

+0

これは本当にうれしいです。私は、この質問が他の良い答えを出す場合には少し質問を残しておきます。 –

+3

'@ contextlib.contextmanager'の使用は便利ですが、手作業で定義された' __enter__'メソッドと '__exit__'メソッドを持つクラスを使うのが適切な場合があります。あなたはそうすることについてアドバイスをしていますか? – Zearin

+0

これは便利です。たとえば、オブジェクトがコンテキストマネージャー以上のことをする必要がある場合です(この場合、@ contextlib.contextmanagerメソッドの追加も考慮する必要があります)。 –

関連する問題