Python contextlib——上下文管理器

来源:互联网 发布:知乎每周精选多久一期 编辑:程序博客网 时间:2024/06/06 02:06

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

文件支持上下文管理器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
>>>classContext(object):
...    def__init__(self):
...        print'__init__()'
...    def__enter__(self):
...        print'__enter__'
...        returnself
...    def__exit__(self,exc_type, exc_val, exc_tb):
...        print'__exit__'
...
>>> with Context():
...    print'Doing work in the context'
...
__init__()
__enter__
Doing work inthe 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
>>>classWithinContext(object):
...    def__init__(self, context):
...        print'WithinContext.__init__(%s)' % context
...    defdo_something(self):
...        print'WithinContext.do_something()'
...    def__del__(self):
...        print'WithinContext.__del__'
...
>>>classContext(object):
...    def__init__(self):
...        print'Context.__init__()'
...    def__enter__(self):
...        print'Context.__enter__()'
...        returnWithinContext(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__.Contextobjectat 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
>>>classContext(object):
...    def__init__(self, handle_error):
...        print'__init__(%s)' % handle_error
...        self.handle_error=handle_error
...    def__enter__(self):
...        print'__enter__()'
...        returnself
...    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
...        returnself.handle_error
...
>>> with Context(True):
...    raiseRuntimeError('error message handled')
...
__init__(True)
__enter__()
__exit__()
   exc_type= <type'exceptions.RuntimeError'>
   exc_val= error message handled
   exc_tb= <traceback objectat 0xb74a334c>
>>>
>>> with Context(False):
...    raiseRuntimeError('error message propagated')
...
__init__(False)
__enter__()
__exit__()
 exc_type= <type'exceptions.RuntimeError'>
 exc_val= error message propagated
exc_tb= <traceback objectat 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
  4import contextlib
  5
  6@contextlib.contextmanager
  7def make_context():
  8    print '   entering'
  9    try:
 10        yield {}
 11    except RuntimeError, err:
 12        print '  ERROR:', err
 13    finally:
 14        print '    exiting'
 15print 'Normal:'
 16with make_context() as value:
 17    print '    inside with statement:', value
 18
 19print '\nHandled error'
 20with make_context() as value:
 21    raise RuntimeError('showing example of handling an error')
 22
 23print '\nUnhandled error:'
 24with 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
 4import contextlib
 5@contextlib.contextmanager
 6def make_context(name):
 7    print ' entering:', name
 8    yield name
 9    print 'exiting:', name
10
11with 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
 4import contextlib
 5class Door(object):
 6    def __init__(self):
 7        print '  __init__()'
 8    def close(self):
 9        print '  close()'
10
11
12print 'Normal Example:'
13with contextlib.closing(Door()) as door:
14    print '  inside with statement'
15
16print '\nError handling example:'
17try:
18    with contextlib.closing(Door()) as door:
19        print '  raiseing from inside with statement'
20        raise RuntimeError('error message')
21except 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块中是否有一个错误,这个句柄都会关闭。
0 0
原创粉丝点击