前言

在读写文件时,为了确保文件流关闭,一般会这么写

# 打开文件
f = open('file.txt')
try:
    for line in f:
        # 读取文件内容 执行其他操作
        # do_something...
finally:
    # 保证关闭文件
    f.close()

但是如果使用with语句就可以

with open('file.txt') as f:
    for line in f:
        # do_something...

上下文管理器

with 的语法格式:

with context_expression [as target(s)]:
    with-body

不过,要想使用 with语法块,with后面的的对象需要实现「上下文管理器协议」。

一个类在 Python 中,只要实现以下方法,就实现了「上下文管理器协议」:

例子

class TestContext:

    def __enter__(self):
        print('__enter__')
        return 1

    # exc_type:异常类型
    # exc_value:异常对象
    # exc_tb:异常堆栈信息
    def __exit__(self, exc_type, exc_value, exc_tb):
        print('exc_type: %s' % exc_type)
        print('exc_value: %s' % exc_value)
        print('exc_tb: %s' % exc_tb)

with TestContext() as t:
    print('t: %s' % t)
    a = 1 / 0

# Output:
# __enter__
# t: 1
# exc_type: <type 'exceptions.ZeroDivisionError'>
# exc_value: integer division or modulo by zero
# exc_tb: <traceback object at 0x10d66dd88>
# Traceback (most recent call last):
#   File "base.py", line 16, in <module>
#     a = 1 / 0
# ZeroDivisionError: integer division or modulo by zero

所以File类的伪代码大概是这样的

class File:

    def __enter__(self):
        return file_obj

    def __exit__(self, exc_type, exc_value, exc_tb):
        # with 退出时释放文件资源
        file_obj.close()
        # 如果 with 内有异常发生 抛出异常
        if exc_type is not None:
            raise exception

使用contextlib模块实现上下文协议