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

 作者:oychw

相关内容

    暂无相关文章

评论关闭