读书人

python模块介绍 contextlib下上文管

发布时间: 2012-09-13 09:51:53 作者: rapoo

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')

print

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

参考资料:

contextlib (http://docs.python.org/library/contextlib.html) 这个模块的标准库文档。

PEP 343 (http://www.python.org/dev/peps/pep-0343) with语句。

ContextManager Types (http://docs.python.org/library/stdtypes.html#type contextmanager) 标准库文档中关于上下文管理器API的描述。

WithStatement Context Managers(http://docs.python.org/reference/datamodel.html#context-managers) Python参考指南中关于上下文管理器API的描述。

The with statement(http://docs.python.org/reference/compound_stmts.html#with)

contextlib—上下文管理器工具(http://book.51cto.com/art/201206/343422.htm)

读书人网 >perl python

热点推荐