2012-01-03 49 views
7

this question中,我定義了一個包含上下文管理器的上下文管理器。什麼是最簡單的方法來完成這種嵌套?我最終打電話self.temporary_file.__enter__()self.__enter__()。但是,在self.__exit__中,我很確定我必須在finally塊中調用self.temporary_file.__exit__(type_, value, traceback)以防發生異常。如果self.__exit__中出現問題,我應該設置type_,value和traceback參數嗎?我檢查了contextlib,但找不到任何工具來幫助解決這個問題。從問題嵌套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。事情是這樣的:

@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塊結束時)。

+0

這真的很不錯。如果這個問題產生任何其他的好答案,我將暫時擱置一會兒。 – 2012-01-04 00:22:33

+3

使用'@ contextlib.contextmanager'很方便,但仍然有些情況下適用於使用手動定義的'__enter__'和'__exit__'方法的類。你有這樣的建議嗎? – Zearin 2014-04-23 16:50:33

+0

好吧,當它更方便的時候 - 例如當對象需要做的不僅僅是一個上下文管理器(儘管在這種情況下你還應該考慮添加一個@ contextlib.contextmanager方法)。 – 2015-09-11 16:08:05