2012-01-03 54 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