下午小夥伴問了一個有趣的問題, 怎麼用 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