上下文管理器对象存在的目的就是管理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()方法被调用
contextlib模块中的使用工具
@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__