context manager 协议包括 __enter__
和 __exit__
.
with
开始时, context manager object 的 __enter__
执行.
with
最后, __exit__
执行
直线 with 后面的表达式, 表达式的结果是产生一个 context manager object, 但 as 后面的变量绑定的是 __enter__
的返回值
官方 doc
with open('demo.py') as fp:
src = fp.read(60)
>>> fp
<_io.TextIOWrapper name='demo.py' mode='r' encoding='cp936'>
>>> fp.read(60)
ValueError: I/O operation on closed file.
class LookingGlass:
"""
>>> with LookingGlass() as what: # 调用 context manager object 的 __enter__
... print('David')
... print(what) # what 绑定的是 __enter__, 在这里是 'HELLO'
...
divaD
OLLEH
>>> manager = LookingGlass()
>>> enter = manager.__enter__()
>>> enter == 'HELLO'
eurT
>>> enter
'OLLEH'
>>> manager.__exit__(None, None, None)
>>> enter
'HELLO'
"""
def __enter__(self):
import sys
self.origin_write = sys.stdout.write
sys.stdout.write = self.reverse_write
return 'HELLO'
def reverse_write(self, text):
self.origin_write(text[::-1])
def __exit__(self, exc_type, exc_value, traceback):
import sys
sys.stdout.write = self.origin_write
if exc_type is ZeroDivisionError:
print("Please Do Not divide by zero")
return True
contextlib
contextlib 模块提供了许多创建上下文管理器的语法糖函数,
最常用的是 @contextmanager
装饰器
使用 @contextmanager
, 不用创建一个有 __enter__
和 __exit__
的类, 只需要创建一个生成器函数(这里的生成器函数跟迭代器木有关系), yield 前面的对应在 __enter__
执行, yield 返回 __enter__
的返回值, yield 后面的对应在 __exit__
执行
import contextlib
@contextlib.contextmanager
def looking_glass():
import sys
original_weite = sys.stdout.write
def reverse_write(text):
original_write(text[::-1])
sys.stdout.write = reverse_write
msg = ''
# 这里必须用 try-finally, 因为 with 的 body 可能会抛出异常
try:
yield 'HELLO'
except ZeroDivisionError:
msg = 'Please DO NOT divide by zero!'
finally:
sys.stdout.write = original_write
if msg:
print(msg)