下午小伙伴问了一个有趣的问题, 怎么用 Python 的 with 语句同时打开多个文件?python
首先, Python 自己是支持同时在 with 中打开多个文件的app
with open('a.txt', 'r') as a, open('b.txt', 'r') as b: print(a.read()) print(b.read())
固然, 小伙伴的问题不可能这么简单, 他须要从打开一个列表中的全部文件, 而不是打开固定的一个文件, 这时候上面的语法显然就无法知足要求了. 幸运的是 with 语句是遵循协议的, 咱们只要自定义出__enter__
方法和__exit__
方法就可使用 with 语句打开自定义的类.好比咱们定义如下类, 用于同时打开多个文件:code
#!/usr/bin/env python # coding: utf-8 class open_many: def __init__(self, files=None, mode='r'): if files is None: self._files = [] else: self._files = files self.mode = mode self.fds = [] # fd is short for file descriptor def __enter__(self): print('-->enter') for f in self._files: print('-->opening file') self.fds.append(open(f, self.mode)) return self.fds def __exit__(self, exc_type, exc_val, traceback): print('-->exit') for fd in self.fds: print('-->closing file') fd.close() if exc_type == ValueError: print('-->exception: ' + str(exc_val)) return True if __name__ == '__main__': print('') with open_many(['a.txt', 'b.txt'], 'r') as files: for f in files: print f.read() print('') with open_many() as files: raise ValueError('captured') print('') with open_many() as files: raise Exception('uncaptureable')
运行结果以下:blog
其中__enter__
方法会被进入 with 语句时调用, 返回值会做为 as 后面变量的值. __exit__
方法的三个参数分别是发生的异常的类型, 异常变量, 和发生异常时的 traceback. 若是在__exit__
中可以处理异常, 那么应该返回True, 不然应该返回 False. 须要注意的是, 并不须要再次把异常抛出. 这里处理的异常实在 with 语句块中发生的异常ip
显然为了使用 with 还须要定义一个类有点过于复杂了, Python 还提供了另外一种方法, 使用 contextlib.contextmanager 装饰器和生成器来实现utf-8
#!/usr/bin/env python # coding: utf-8 from contextlib import contextmanager @contextmanager def open_many(files=None, mode='r'): if files is None: files = [] try: #至关于__enter__ fds = [] for f in files: fds.append(open(f, mode)) yield fds except ValueError as e: print(e) finally: #至关于__exit__ for fd in fds: fd.close() if __name__ == '__main__': with open_many(['a.txt', 'b.txt'], 'r') as files: for f in files: print(f.read())
此外, contextlib 中还提供了 closing 语句, 使用这个语句能够自动调用类的 close 方法.it
#!/usr/bin/env python # coding: utf-8 from contextlib import closing class Connection: def open(self): print('opened') def close(self): print('closed') if __name__ == '__main__': with closing(Connection()) as con: con.open() # do stuff with con