上下文管理器

上下文管理器对象存在的目的是管理with语句,就像迭代器的存在是为了管理for语句。


上下文管理器协议包含__enter__和__exit__两个方法。

with 语句开始运行时,会在上下文管理器对象上调用 __enter__ 方法。with 语句运行结束后,会在上下文管理器对象上调用 __exit__ 方法。


举个例子:



import sys
class LookingClass:
def __enter__(self):
self.original_write = sys.stdout.write
sys.stdout.write = self.reverse_write
return 'JABBERWOCKY'
def reverse_write(self, text):
return self.original_write(text[::-1])
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout.write = self.original_write
if exc_type is ZeroDivisionError:
print('Please DO NOT divide by zero!')
return True
# 1 测试LookingClass
with LookingClass() as what:
print('Alice, Kitty and Snowdrop')
print(what)
# 这时with已经吧LookingClass关闭了
print('Back to normal.')
# 2 直接实例化LookingClass
manager = LookingClass()
print(manager)
print(manager.__enter__())
print(manager) # 没有退出上下文管理器
print(manager.__exit__(None, None, None))
print(manager) # 恢复正常



使用@contextmanager

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


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


这个类的 __enter__ 方法有如下作用。

(1) 调用生成器函数,保存生成器对象(这里把它称为 gen)。

(2) 调用 next(gen),执行到 yield 关键字所在的位置。

(3) 返回 next(gen) 产出的值,以便把产出的值绑定到 with/as 语句中的目标变量上。


with 块终止时,__exit__ 方法会做以下几件事。

(1) 检查有没有把异常传给 exc_type;如果有,调用 gen.throw(exception),在生成器函数

定义体中包含 yield 关键字的那一行抛出异常。

(2) 否则,调用 next(gen),继续执行生成器函数定义体中 yield 语句之后的代码。


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


举个例子:


import sys
import contextlib
@contextlib.contextmanager
def looking_class():
original_value = sys.stdout.write
def reverse_write(text):
return original_value(text[::-1])
sys.stdout.write = reverse_write
# 增加处理__exit__方法时的异常
msg = ''
try:
yield 'JABBERWOCKY'
except ZeroDivisionError:
msg = 'Please DO NOT divide by zero!'
finally:
sys.stdout.write = original_value
if msg:
print(msg)
with looking_class() as wp:
print('I love Python')
print(wp)




人生的路还很长,继续走吧