上下文管理器和with块

上下文管理器

上下文管理器对象存在的目的是管理 with 语句, 就像迭代器的存在是为了管理 for 语句一样。with 语句的目的是简化 try/finally 模式。 这种模式用于保证一段代码运行完毕后执行某项操作, 即便那段代码由于异常、 return 语句或sys.exit() 调用而中止, 也会执行指定的操作。 finally 子句中的代码通常用于释放重要的资源, 或者还原临时变更的状态。


        上下文管理器协议包含 __enter__ __exit__ 两个方法。 with 语句开始运行时, 会在上下文管理器对象上调用 __enter__ 方法。 with 语句运行结束后, 会在上下文管理器对象上调用 __exit__ 方法, 以此扮演 finally 子句的角色。

示例一:把文件对象当成上下文管理器使用

with open('data.py') as fp: #执行with 后面的表达式得到的结果是上下文管理器对象, 不过, 把值绑定
#到目标变量上(as 子句) 是在上下文管理器对象上调用 __enter__ 方法的结果。

    src=fp.read(60)

print(src)
print(fp) #可以读取fp对象,但是不能进行I/O操作,因为在with块的末尾,调用TextIOWrapper.__exit__方法把文件给关闭了
'''
不管控制流程以哪种方式退出 with 块, 都会在上下文管理器对象上调用 __exit__ 方法, 而不是在 __enter__ 方法返回的对象上调用。
'''

示例二:

class LookingGlass:
    def __enter__(self):
        import sys
        self.original_write = sys.stdout.write  # 把原来的 sys.stdout.write 方法保存在一个实例属性中,供后面使用。
        sys.stdout.write = self.reverse_write  # 猴子补丁,替换字符串反转方法
        return 'JABBERWOCKY'  # 返回字符串,存入what

    def reverse_write(self, text):  # 自定义的字符串反转方法
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback):
        # 如果一切正常, Python 调用 __exit__ 方法时传入的参数是 None,None, None; 如果抛出了异常, 这三个参数是异常数据
        import sys
        sys.stdout.write = self.original_write
        if exc_type is ZeroDivisionError:
            print('Please DO NOT divide by zero!')
        return True


with LookingGlass() as what:  # Python 在上下文管理器上调用 __enter__ 方法, 把返回结果绑定到 what 上。
    print('Alice, Kitty and Snowdrop')
    print(what)

 结果:

 解释器调用 __enter__ 方法时, 除了隐式的 self 之外, 不会传入任何参数。 传给 __exit__ 方法的三个参数列举如下。
exc_type
    异常类(例如 ZeroDivisionError) 。
exc_value
    异常实例。 有时会有参数传给异常构造方法, 例如错误消息, 这些
    参数可以使用 exc_value.args 获取。
traceback
    traceback 对象

使用@contextmanager

@contextmanager
这个装饰器把简单的生成器函数变成上下文管理器, 这样就不用创建类去实现管理器协议了。

@contextmanager 装饰器能减少创建上下文管理器的样板代码量, 因为不用编写一个完整的类, 定义 __enter__ 和 __exit__ 方法, 而只需实现有一个 yield 语句的生成器, 生成想让 __enter__ 方法返回的值

在使用 @contextmanager 装饰的生成器中, yield 语句的作用是把函数的定义体分成两部分: yield 语句前面的所有代码在 with 块开始时(即解释器调用 __enter__ 方法时) 执行 yield 语句后面的代码在with 块结束时(即调用 __exit__ 方法时) 执行。
 

上面的示例使用@contextmanager 装饰器

import contextlib


@contextlib.contextmanager
def looking_glass():
    import sys
    original_write = sys.stdout.write

    def reverse_write(text):
        original_write(text[::-1])

    sys.stdout.write = reverse_write
    msg = ''
    try:
        yield 'JABBERWOCKY'  # 产出一个值, 这个值会绑定到 with 语句中 as 子句的目标变量上。
    except ZeroDivisionError:
        msg = 'Please DO NOT divide by zero!'
    finally:
        sys.stdout.write = original_write
        if msg:
            print(msg)

@contextmanager 装饰器优雅且实用, 把三个不同的 Python 特性结合到了一起: 函数装饰器、 生成器和 with 语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值