Python contextlib——上下文管理器

上下文管理器要負責一個代碼塊中的資源,可能在進入代碼塊時建立資源,而後在退出代碼塊時清理這個資源。 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塊中是否有一個錯誤,這個句柄都會關閉。
相關文章
相關標籤/搜索