python模块介绍 contextlib—上下文管理器工具
contextlib—上下文管理器工具
2012-07-27 磁针石
#承接软件自动化实施与培训等gtalk: ouyangchongwu#gmail.com qq 37391319博客:testing.blog.chinaunix.net
#版权所有,转载刊登请来函联系
#自动化测试和python群组: http://groups.google.com/group/automation_testing_python
#参考资料:《ThePython Standard Library by Example》
# 实验环境:Python2.7.3 CentOS release 6.2 (Final) 32bits
3.4 contextlib—上下文管理器工具
作用:创建和处理上下文管理器的工具。
Python版本:2.5及以后版本
contextlib模块包含一些工具,用于处理上下文管理器和with语句。上下文管理器和with语句,可以减少try:finally块的个数和所需的缩进层次。
注意:上下文管理器要与with语句关联。由于官方认为with是Python 2.6的一部分,因此在Python2.5中使用contextlib之前要从__future__将其导入。比如from__future__ import with_statement。
3.4.1 上下文管理器API
上下文管理器(context manager)要负责一个代码块中的资源,可能在进入代码块时创建资源,然后在退出代码块时清理这个资源。例如,文件支持上下文管理器API,可以很容易地确保完成文件读写后关闭文件。
with open(’/tmp/pymotw.txt’,’wt’) as f:
f.write(’contents go here’)
# file isautomatically closed
上下文管理器由with语句启用,这个API包括两个方法。当执行流进入with中的代码块时会运行__enter__()方法。它会返回一个对象,在这个上下文中使用。当执行流离开with块时,则调用这个上下文管理器的__exit__()方法来清理所使用的资源。
#!/usr/bin/envpython
# encoding:utf-8
#
# Copyright(c) 2010 Doug Hellmann. All rightsreserved.
#
"""Implementingthe context manager API by hand.
"""
#end_pymotw_header
classContext(object):
def __init__(self):
print '__init__()'
def __enter__(self):
print '__enter__()'
return self
def __exit__(self, exc_type, exc_val,exc_tb):
print '__exit__()'
withContext():
print 'Doing work in the context'
结合上下文管理器与with语句是try:finally块的一种更紧凑的写法,因为上下文管理器的__exit__()方法总会调用,即使在产生异常的情况下也是如此。执行结果如下:
# python contextlib_api.py
__init__()
__enter__()
Doing work inthe context
__exit__()
如果在with语句的as子句中指定了名称,__enter__()方法可以返回与这个名称相关联的任何对象。在这个例子中,Context会返回使用了上下文的对象。
#!/usr/bin/env python
# encoding:utf-8
#
# Copyright(c) 2010 Doug Hellmann. All rightsreserved.
#
"""Implementingthe context manager API by hand.
"""
#end_pymotw_header
classWithinContext(object):
def __init__(self, context):
print 'WithinContext.__init__(%s)' %context
def do_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__()'
return WithinContext(self)
def __exit__(self, exc_type, exc_val,exc_tb):
print 'Context.__exit__()'
withContext() as c:
c.do_something()
执行结果:
pythoncontextlib_api_other_object.py
Context.__init__()
Context.__enter__()
WithinContext.__init__(<__main__.Contextobject at 0xb77684cc>)
WithinContext.do_something()
Context.__exit__()
WithinContext.__del__
__exit__()方法接收一些参数,其中包含with块中产生的异常的详细信息
#!/usr/bin/env python
# encoding:utf-8
#
# Copyright(c) 2010 Doug Hellmann. All rightsreserved.
#
"""Implementingthe context manager API by hand.
"""
#end_pymotw_header
classContext(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
withContext(True):
raise RuntimeError('error message handled')
withContext(False):
raiseRuntimeError('error message propagated')
如果上下文管理器可以处理这个异常,__exit__() 应当返回一个true值来指示不需要传播这个异常。如果返回false,就会导致__exit__()返回后重新抛出这个异常。执行结果如下:
# pythoncontextlib_api_error.py
__init__(True)
__enter__()
__exit__()
exc_type = <type'exceptions.RuntimeError'>
exc_val = error message handled
exc_tb = <traceback object at 0xb7691504>
__init__(False)
__enter__()
__exit__()
exc_type = <type'exceptions.RuntimeError'>
exc_val = error message propagated
exc_tb = <traceback object at 0xb769157c>
Traceback(most recent call last):
File "contextlib_api_error.py",line 30, in <module>
raise RuntimeError('error messagepropagated')
RuntimeError:error message propagated
3.4.2从生成器到上下文管理器
采用传统方式创建上下文管理器,即编写一个包含__enter__()和__exit__()方法的类,这并不难。不过有些时候,对于很少的上下文来说, 完全编写所有代码会是额外的负担。在这些情况下,可以使用contextmanager()修饰符将一个生成器函数转换为上下文管理器。
#!/usr/bin/envpython
# encoding:utf-8
#
# Copyright(c) 2008 Doug Hellmann All rights reserved.
#
"""
$Id$
"""
#end_pymotw_header
importcontextlib
@contextlib.contextmanager
defmake_context():
print ' entering'
try:
yield {}
except RuntimeError, err:
print ' ERROR:', err
finally:
print ' exiting'
print'Normal:'
withmake_context() as value:
print ' inside with statement:', value
print'/nHandled error:'
withmake_context() as value:
raise RuntimeError('showing example ofhandling an error')
print'/nUnhandled error:'
withmake_context() as value:
raise ValueError('this exception is nothandled')
生成器要初始化上下文,用yield生成一次值,然后清理上下文。所生成的值(如果有)会绑定到with语句as子句中的变量。with块中的异常会在生成器中重新抛出,使之在生成器中得到处理。执行结果:
# pythoncontextlib_contextmanager.py
Normal:
entering
inside with statement: {}
exiting
Handlederror:
entering
ERROR: showing example of handling an error
exiting
Unhandlederror:
entering
exiting
Traceback(most recent call last):
File"contextlib_contextmanager.py", line 32, in <module>
raise ValueError('this exception is nothandled')
ValueError:this exception is not handled
3.4.3嵌套上下文
有些情况下,必须同时管理多个上下文(例如在输入和输出文件句柄之间复制数据)。为此可以嵌套with语句,不过,如果外部上下文并不需要单独的块,嵌套 with语句就只会增加缩进层次而不会提供任何实际的好处。使用nested()可以利用一条with语句实现上下文嵌套。
#!/usr/bin/envpython
# encoding:utf-8
#
# Copyright(c) 2008 Doug Hellmann All rights reserved.
#
"""
$Id$
"""
#end_pymotw_header
importcontextlib
@contextlib.contextmanager
defmake_context(name):
print 'entering:', name
yield name
print 'exiting :', name
withcontextlib.nested(make_context('A'),
make_context('B')) as (A,B):
print 'inside with statement:', A, B
程序执行时会按其进入上下文的逆序离开上下文。
# pythoncontextlib_nested.py
entering: A
entering: B
inside withstatement: A B
exiting : B
exiting : A
在Python 2.7及以后版本中废弃了nested(),因为在这些版本中with语句直接支持嵌套。
#!/usr/bin/envpython
# encoding:utf-8
#
# Copyright(c) 2008 Doug Hellmann All rights reserved.
#
"""
$Id$
"""
#end_pymotw_header
importcontextlib
@contextlib.contextmanager
defmake_context(name):
print 'entering:', name
yield name
print 'exiting :', name
withmake_context('A') as A, make_context('B') as B:
print 'inside with statement:', A, B
每个上下文管理器与as子句(可选)之间用一个逗号(,)分隔。其效果类似于使用nested(),不过避免了nested()不能正确实现的有关错误处理的一些边界情况。
# pythoncontextlib_nested_with.py
entering: A
entering: B
inside withstatement: A B
exiting : B
exiting : A
3.4.4 关闭打开的句柄
file类直接支持上下文管理器API,不过表示打开句柄的另外一些对象并不支持这个API。contextlib的标准库文档中给出的例子是一个由 urllib.urlopen()返回的对象。还有另外一些遗留类,它们使用close()方法而不支持上下文管理器API。为了确保关闭句柄,需要使用 closing()为它创建一个上下文管理器。
#!/usr/bin/envpython
# encoding:utf-8
#
# Copyright(c) 2008 Doug Hellmann All rights reserved.
#
"""
$Id$
"""
#end_pymotw_header
importcontextlib
classDoor(object):
def __init__(self):
print ' __init__()'
def close(self):
print ' close()'
print 'NormalExample:'
withcontextlib.closing(Door()) as door:
print ' inside with statement'
print'/nError handling example:'
try:
with contextlib.closing(Door()) as door:
print ' raising from inside with statement'
raise RuntimeError('error message')
exceptException, err:
print ' Had an error:', err
不论with块中是否有一个错误,这个句柄都会关闭。执行结果:
# pythoncontextlib_closing.py
NormalExample:
__init__()
inside with statement
close()
Errorhandling example:
__init__()
raising from inside with statement
close()
Had an error: error message
作者:oychw