博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python contextlib——上下文管理器
阅读量:4300 次
发布时间:2019-05-27

本文共 5471 字,大约阅读时间需要 18 分钟。

上下文管理器要负责一个代码块中的资源,可能在进入代码块时创建资源,然后在退出代码块时清理这个资源。

文件支持上下文管理器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__
>>>
使用as创建with语句中__enter__()返回的对象别名
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
>>>
如果上下文可以处理这个异常,__exit__() 应当返回一个true值来指示不需要传播这个异常,如果返回false,就会导致__exit__()返回后重新抛出这个异常。

从生成器到上下文管理器
使用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
程序执行时会按其进入上下文的逆序离开上下文。每个上下文管理器与as子句(可选)之间用一个逗号(,)分隔。

关闭打开的句柄
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
不论with块中是否有一个错误,这个句柄都会关闭。

转载地址:http://pxvws.baihongyu.com/

你可能感兴趣的文章
Java多线程-56-简单工厂设计模式
查看>>
Java多线程-57-工厂方法设计模式
查看>>
Docker基础-1-Docker概述
查看>>
Docker基础-2-Docker在win10上安装过程
查看>>
Docker基础-3-Docker在CentOS上安装过程
查看>>
Docker基础-4-Docker架构和底层技术实现初探
查看>>
Docker基础-5-image概述
查看>>
Docker基础-6-如何配置docker从中国官网仓库下载,提升镜像拉取速度
查看>>
Docker基础-7-使用Dockerfile DIY第一个镜像
查看>>
Docker基础-8-初识container
查看>>
Docker基础-9-docker命令(一)
查看>>
Docker基础-13-如何发布一个镜像到私有的docker仓库
查看>>
Docker基础-14-Dockerfile实战练习2
查看>>
Docker基础-15-容器操作
查看>>
Docker基础-16-网络-Linux网络命名空间
查看>>
Docker基础-17-网络-两个容器为什么能通信
查看>>
Docker基础-18-网络-两个网络命名空间网络通信配置过程
查看>>
Docker基础-19-网络-bridge模式和docker0详解
查看>>
Docker基础-20-网络-容器link关系和新建bridge网络
查看>>
Docker基础-21-网络-none和host网络
查看>>