Python中使用with語句同時打開多個文件

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

result

其中__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
相關文章
相關標籤/搜索