本文共 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__ >>> |
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 >>> |
从生成器到上下文管理器
使用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 |
关闭打开的句柄
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 |
转载地址:http://pxvws.baihongyu.com/