上下文管理器要负责一个代码块中的资源,可能在进入代码块时创建资源,然后在退出代码块时清理这个资源。
文件支持上下文管理器API,可以很容易地确保完成文件读写后关闭文件
1
2
3
|
>>> with
open
(
'/tmp/file.txt'
,
'wt'
) as f:
... f.write(
'continue to goa here'
)
...
|
上下文管理器由with语句启用,这个API包括两个方法。当执行流进入with中的代码块时会运行__enter__()方法。它会返回一个对象,在这个上下文中使用。当执行流离开with块时,则调用这个上下文管理器的__exit__()方法来清理所使用的资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
>>>
class
Context(
object
):
...
def
__init__(
self
):
...
print
'__init__()'
...
def
__enter__(
self
):
...
print
'__enter__'
...
return
self
...
def
__exit__(
self
,exc_type, exc_val, exc_tb):
...
print
'__exit__'
...
>>> with Context():
...
print
'Doing work in the context'
...
__init__()
__enter__
Doing work
in
the context
__exit__
>>>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
>>>
class
WithinContext(
object
):
...
def
__init__(
self
, context):
...
print
'WithinContext.__init__(%s)'
%
context
...
def
do_something(
self
):
...
print
'WithinContext.do_something()'
...
def
__del__(
self
):
...
print
'WithinContext.__del__'
...
>>>
class
Context(
object
):
...
def
__init__(
self
):
...
print
'Context.__init__()'
...
def
__enter__(
self
):
...
print
'Context.__enter__()'
...
return
WithinContext(
self
)
...
def
__exit__(
self
, exc_type, exc_val, exc_tb):
...
print
'Context.__exit__()'
...
>>> with Context() as c:
... c.do_something()
...
Context.__init__()
Context.__enter__()
WithinContext.__init__(<__main__.Context
object
at
0xb74a2f6c
>)
WithinContext.do_something()
Context.__exit__()
>>>
|
与变量c关联的值是__enter__()返回的对象,这不一定是with语句中创建的Context实例。
__exit__()方法接受一些参数,其中包含with块中产生的异常的详细信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
>>>
class
Context(
object
):
...
def
__init__(
self
, handle_error):
...
print
'__init__(%s)'
%
handle_error
...
self
.handle_error
=
handle_error
...
def
__enter__(
self
):
...
print
'__enter__()'
...
return
self
...
def
__exit__(
self
, exc_type, exc_val, exc_tb):
...
print
'__exit__()'
...
print
' exc_type = '
, exc_type
...
print
' exc_val = '
, exc_val
...
print
' exc_tb = '
, exc_tb
...
return
self
.handle_error
...
>>> with Context(
True
):
...
raise
RuntimeError(
'error message handled'
)
...
__init__(
True
)
__enter__()
__exit__()
exc_type
=
<
type
'exceptions.RuntimeError'
>
exc_val
=
error message handled
exc_tb
=
<traceback
object
at
0xb74a334c
>
>>>
>>> with Context(
False
):
...
raise
RuntimeError(
'error message propagated'
)
...
__init__(
False
)
__enter__()
__exit__()
exc_type
=
<
type
'exceptions.RuntimeError'
>
exc_val
=
error message propagated
exc_tb
=
<traceback
object
at
0xb74ce20c
>
Traceback (most recent call last):
File
"<stdin>"
, line
2
,
in
<module>
RuntimeError: error message propagated
>>>
|
从生成器到上下文管理器
使用contextmanager()修饰符将一个生成器函数转换成上下文管理器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
1
#! /usr/bin/python
2
# -*- coding:utf-8 -*-
3
4
import
contextlib
5
6
@contextlib.contextmanager
7
def
make_context():
8
print
' entering'
9
try
:
10
yield
{}
11
except
RuntimeError, err:
12
print
' ERROR:'
, err
13
finally
:
14
print
' exiting'
15
print
'Normal:'
16
with make_context() as value:
17
print
' inside with statement:'
, value
18
19
print
'\nHandled error'
20
with make_context() as value:
21
raise
RuntimeError(
'showing example of handling an error'
)
22
23
print
'\nUnhandled error:'
24
with make_context() as value:
25
raise
ValueError(
'this exception is not handled'
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Normal:
entering
inside with statement: {}
exiting
Handled error
entering
ERROR: showing example of handling an error
exiting
Unhandled error:
entering
exiting
Traceback (most recent call last):
File
"contextlib_contextmanager.py"
, line 25,
in
<module>
raise ValueError(
'this exception is not handled'
)
ValueError: this exception is not handled
|
生成器要初始化上下文,用yield生成一次值,然后清理上下文。所生成的值(如果有)会绑定到with语句as子句中的变量。with块中的异常会在生成器中重新抛出,使之在生成器中得到处理。
嵌套上下文
1
2
3
4
5
6
7
8
9
10
11
12
|
1
#! /usr/bin/python
2
# -*- coding:utf-8 -*-
3
4
import
contextlib
5
@contextlib.contextmanager
6
def
make_context(name):
7
print
' entering:'
, name
8
yield
name
9
print
'exiting:'
, name
10
11
with make_context(
'A'
) as A, make_context(
'B'
) as B:
12
print
'inside with statement:'
, A,B
|
1
2
3
4
5
|
entering: A
entering: B
inside with statement: A B
exiting: B
exiting: A
|
关闭打开的句柄
file类直接支持上下文管理器API,不过表示打开句柄的另外一些对象并不支持这个API。contextlib的标准库文档给出的例子是一个由urllib.urlopen()返回的对象。还有一些遗留类,他们使用close()方法而不支持上下文管理器API。为了确保关闭句柄,需要使用closing()为它创建一个上下文管理器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
1
#! /usr/bin/python
2
# -*- coding:utf-8 -*-
3
4
import
contextlib
5
class
Door(
object
):
6
def
__init__(
self
):
7
print
' __init__()'
8
def
close(
self
):
9
print
' close()'
10
11
12
print
'Normal Example:'
13
with contextlib.closing(Door()) as door:
14
print
' inside with statement'
15
16
print
'\nError handling example:'
17
try
:
18
with contextlib.closing(Door()) as door:
19
print
' raiseing from inside with statement'
20
raise
RuntimeError(
'error message'
)
21
except
Exception, err:
22
print
' Had an error:'
, err
|
1
2
3
4
5
6
7
8
9
10
|
Normal Example:
__init__()
inside with statement
close()
Error handling example:
__init__()
raiseing from inside with statement
close()
Had an error: error message
|