Python 中的上下文管理器

Python 中的上下文管理器

with expression [as target]:
    with-body

上下文管理器是为with 语句而生。只要实现了上下文管理器协议__enter____exit__,就可以使用with语句。

__enter__通常执行一些初始化操作,并且该函数的返回值会赋值给可选的 as target 中的target变量。

__exit__执行资源清理工作。它接收三个参数,异常类型,异常实例,和异常栈,根据这些异常信息,__exit__可以选择进行相应的异常处理,并默认抛出异常。如果我们在让__exit__返回True,相当于告诉python:这些异常我都已经处理了,都在掌控之中,您老不必操心。

除了自定义类手动实现两个特殊方法外,还有另一种途径实现一个上下文管理器。
标准库contextlib中提供了一个@contextmanager可以方便的把一个协程函数包装成一个上下文管理器。
《Fluent Python》 书中一个好玩的例子:

@contextmanager
def f():
    import sys
    print('欢迎来到镜像的世界')
    origin_print = sys.stdout.write
    sys.stdout.write = lambda x: origin_print(x[::-1])  
    # 初始化:替换系统输入。运行中动态修改、添加类的方法————猴子补丁。
    yield '这里的打印都是反向输出'
    sys.stdout.write = origin_print  # 退出时:恢复系统输入
    print('Finally I come back')

mirror_world = f()
with mirror_world as target:
    print(target)

输出结果:

欢迎来到镜像的世界
出输向反是都印打的里这
Finally I come back

协程函数中yield之前的所有代码相当于__enter__部分的工作,执行初始化,执行中动态替换了系统的输出功能(猴子补丁特性)。
并且把一个结果绑定到with...as targettarget。至此协程函数交出代码执行权,python转而去执行with-block里面的代码。执行完with-block 开始执行yield之后的代码——相当于__exit__的工作,执行资源清理。

至此我们好像实现了一个功能正常的上下文管理器。但别忘了还有异常捕获的机制。。。

在终端中执行mirror_world时,如果with-block中抛出了一个异常,会导致资源清理工作没有进行,之后所有的print仍是反向输出。我们还应做的是把yield行的代码包裹在一个try...except...finally中,在finally-bolck中执行资源清理工作,以保证正常退出(鬼知道用户会在with-block搞什么蛇皮…)。

你可能感兴趣的:(学习之路)