上下文管理器对象存在的目的就是管理with语句。上下文管理器协议包含__enter__ 和__exit__ 两个方法。with 语句开始运行时,会在上下文管理器对象上调用__enter__ 方法。with 语句运行结束后,会在上下文管理器对象上调用__exit__ 方法
来看一个例子,把文件对象当成上下文管理器使用
with open('test.dat') as fp:
secc = fp.read(20)
>>> len(src)
20
>>> fp
<_io.TextIOWrapper name='mirror.py' mode='r' encoding='UTF-8'>
>>> fp.closed, fp.encoding
(True, 'UTF-8')
"""
但是不能在fp 上执行I/O 操作,因为在with 块的末尾,调用TextIOWrapper.__exit__
方法把文件关闭了。
"""
>>> fp.read(60)
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: I/O operation on closed file.
执行with后面的表达式得到的结果是上下文管理器对象,不过,把值绑定到目标变量上(as子句)是上下文管理器调用__enter__方法的结果
来看一个例子,自己实现一个上下文管理器
class Sample():
def __enter__(self):
print("in __enter__")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("in __exit__")
def do_something(self):
print("i am doing something")
>>> with Sample() as sample:
sample.do_something()
in __enter__
i am doing something
in __exit__
真个过程运行如下,enter()方法被执行,enter()方法返回值,在这里是类自身,执行代码块sample.do_something(), 最后是exit()方法被调用
@contextmanager 装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义__enter__ 和__exit__方法,而只需实现有一个yield 语句的生成器,生成想让__enter__ 方法返回的值。
在使用@contextmanager 装饰的生成器中,yield语句的作用是把函数的定义体分成两部分**:yield 语句前面的所有代码在with 块开始时(即解释器调用__enter__方法时)执行,yield 语句后面的代码在with 块结束时(即调用__exit__ 方法时)执行**
看一个使用contextmanager实现上下文管理器的例子
import contextlib
@contextlib.contextmanager
def Sample():
def do_something():
print('i am doing something')
print("in __enter__")
yield do_something
print("in exit")
>>> with Sample() as sample:
sample()
in __enter__
i am doing something
in __exit__