上下文管理器要負責一個代碼塊中的資源,可能在進入代碼塊時建立資源,而後在退出代碼塊時清理這個資源。 python
文件支持上下文管理器API,能夠很容易地確保完成文件讀寫後關閉文件 shell
>>> with open('/tmp/file.txt','wt') as f: ... f.write('continue to goa here') ...
上下文管理器由with語句啓用,這個API包括兩個方法。當執行流進入with中的代碼塊時會運行__enter__()方法。它會返回一個對象,在這個上下文中使用。當執行流離開with塊時,則調用這個上下文管理器的__exit__()方法來清理所使用的資源。 ide
>>> 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__ >>>使用as建立with語句中__enter__()返回的對象別名
>>> 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塊中產生的異常的詳細信息。 this
>>> 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 >>>若是上下文能夠處理這個異常,__exit__() 應當返回一個true值來指示不須要傳播這個異常,若是返回false,就會致使__exit__()返回後從新拋出這個異常。
從生成器到上下文管理器
使用contextmanager()修飾符將一個生成器函數轉換成上下文管理器。 url
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')輸出結果:
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塊中的異常會在生成器中從新拋出,使之在生成器中獲得處理。 code
嵌套上下文 orm
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輸出結果
entering: A entering: B inside with statement: A B exiting: B exiting: A程序執行時會按其進入上下文的逆序離開上下文。每一個上下文管理器與as子句(可選)之間用一個逗號(,)分隔。
關閉打開的句柄
file類直接支持上下文管理器API,不過表示打開句柄的另一些對象並不支持這個API。contextlib的標準庫文檔給出的例子是一個由urllib.urlopen()返回的對象。還有一些遺留類,他們使用close()方法而不支持上下文管理器API。爲了確保關閉句柄,須要使用closing()爲它建立一個上下文管理器。 對象
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輸出結果:
Normal Example: __init__() inside with statement close() Error handling example: __init__() raiseing from inside with statement close() Had an error: error message不論with塊中是否有一個錯誤,這個句柄都會關閉。