詳解Python中contextlib上下文管理模塊的用法
咱們用的os模塊,讀取文件的時候,其實(shí)他是含有__enter__ __exit__ 。 一個是with觸發(fā)的時候,一個是退出的時候。
with file('nima,'r') as f: print f.readline()
那咱們自己再實(shí)現(xiàn)一個標(biāo)準(zhǔn)的可以with的類。 我個人寫python的時候,喜歡針對一些需要有關(guān)閉邏輯的代碼,構(gòu)造成with的模式 。
#encoding:utf-8 class echo: def __enter__(self): print 'enter' def __exit__(self,*args): print 'exit' with echo() as e: print 'nima'
contextlib是個比with優(yōu)美的東西,也是提供上下文機(jī)制的模塊,它是通過Generator裝飾器實(shí)現(xiàn)的,不再是采用__enter__和__exit__。contextlib中的contextmanager作為裝飾器來提供一種針對函數(shù)級別的上下文管理機(jī)制。
from contextlib import contextmanager @contextmanager def make_context() : print 'enter' try : yield {} except RuntimeError, err : print 'error' , err finally : print 'exit' with make_context() as value : print value
我這里再貼下我上次寫的redis分布式鎖代碼中有關(guān)于contextlib的用法。其實(shí)乍一看,用了with和contextlib麻煩了,但是最少讓你的主體代碼更加鮮明了。
from contextlib import contextmanager from random import random DEFAULT_EXPIRES = 15 DEFAULT_RETRIES = 5 @contextmanager def dist_lock(key, client): key = 'lock_%s' % key try: _acquire_lock(key, client) yield finally: _release_lock(key, client) def _acquire_lock(key, client): for i in xrange(0, DEFAULT_RETRIES): get_stored = client.get(key) if get_stored: sleep_time = (((i+1)*random()) + 2**i) / 2.5 print 'Sleeipng for %s' % (sleep_time) time.sleep(sleep_time) else: stored = client.set(key, 1) client.expire(key,DEFAULT_EXPIRES) return raise Exception('Could not acquire lock for %s' % key) def _release_lock(key, client): client.delete(key)
Context Manager API
一個上下文管理器通過with聲明激活, 而且API包含兩個方法。__enter__()方法運(yùn)行執(zhí)行流進(jìn)入到with代碼塊內(nèi)。他返回一個對象共上下文使用。當(dāng)執(zhí)行流離開with塊時,__exit__()方法上下文管理器清除任何資源被使用。
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.'
打印結(jié)果
__init__() __enter__() Doing work in the context. __exit__()
執(zhí)行上下文管理器時會調(diào)用__enter__離開時調(diào)用__exit__。
__enter__能返回任意對象,聯(lián)合一個指定名字于with聲明。
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 '__init__()' def __enter__(self): print '__enter__()' return WithinContext(self) def __exit__(self, exc_type, exc_val, exc_tb): print '__exit__()' with Context() as c: c.do_something()
打印結(jié)果
__init__() __enter__() WithinContext.__init__(<__main__.Context object at 0x7f579d8e4890>) WithinContext.do_something() __exit__() WithinContext.__del__
如果上下文管理器能處理異常,__exit__()應(yīng)該返回一個True值表明這個異常不需要傳播,返回False異常會在執(zhí)行__exit__之后被引起。
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__(%s, %s, %s)' % (exc_type, exc_val, exc_tb) return self.handle_error with Context(True): raise RuntimeError('error message handled') print with Context(False): raise RuntimeError('error message propagated')
打印結(jié)果
__init__(True) __enter__() __exit__(<type 'exceptions.RuntimeError'>, error message handled, <traceback object at 0x7fdfb32f8b00>) __init__(False) __enter__() __exit__(<type 'exceptions.RuntimeError'>, error message propagated, <traceback object at 0x7fdfb32f8b90>) Traceback (most recent call last): File "test.py", line 23, in <module> raise RuntimeError('error message propagated') RuntimeError: error message propagated
從生成器到上下文管理器
創(chuàng)建上下文管理的傳統(tǒng)方法,通過編寫一個類與__enter__()和__exit__()方法,并不困難。但有時比你需要的開銷只是管理一個微不足道的上下文。在這類情況下,您可以使用contextmanager() decorat or 生成器函數(shù)轉(zhuǎn)換成一個上下文管理器。
import contextlib @contextlib.contextmanager def make_context(): print ' entering' try: yield {} except RuntimeError, err: print ' Error:', err finally: print ' exiting' print 'Normal:' with make_context() as value: print ' inside with statement:', value print print 'handled ereor:' with make_context() as value: raise RuntimeError('show example of handling an error') print print 'unhandled error:' with make_context() as value: raise ValueError('this exception is not handled')
打印結(jié)果
Normal: entering inside with statement: {} exiting handled ereor: entering Error: show example of handling an error exiting unhandled error: entering exiting Traceback (most recent call last): File "test.py", line 30, in <module> raise ValueError('this exception is not handled') ValueError: this exception is not handled
嵌套上下文
使用nested()可以同時管理多個上下文。
import contextlib @contextlib.contextmanager def make_context(name): print 'entering:', name yield name print 'exiting:', name with contextlib.nested(make_context('A'), make_context('B'), make_context('C')) as (A, B, C): print 'inside with statement:', A, B, C
打印結(jié)果
entering: A entering: B entering: C inside with statement: A B C exiting: C exiting: B exiting: A
因?yàn)镻ython 2.7和以后的版本不贊成使用nested(),因?yàn)榭梢灾苯忧短?/p>
import contextlib @contextlib.contextmanager def make_context(name): print 'entering:', name yield name print 'exiting:', name with make_context('A') as A, make_context('B') as B, make_context('C') as C: print 'inside with statement:', A, B, C
關(guān)閉open的句柄
文件類支持上下文管理器, 但是有一些對象不支持。還有一些類使用close()方法但是不支持上下文管理器。我們使用closing()來為他創(chuàng)建一個上下文管理器。(類必須有close方法)
import contextlib class Door(object): def __init__(self): print ' __init__()' def close(self): print ' close()' print 'Normal Example:' with contextlib.closing(Door()) as door: print ' inside with statement' print print 'Error handling example:' try: with contextlib.closing(Door()) as door: print ' raising from inside with statement' raise RuntimeError('error message') except Exception, err: print ' Had an error:', err
打印結(jié)果
Normal Example: __init__() inside with statement close() Error handling example: __init__() raising from inside with statement close() Had an error: error message
相關(guān)文章
python 通過類中一個方法獲取另一個方法變量的實(shí)例
今天小編就為大家分享一篇python 通過類中一個方法獲取另一個方法變量的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01Python2和Python3中urllib庫中urlencode的使用注意事項(xiàng)
這篇文章主要介紹了Python2和Python3中urllib庫中urlencode的使用注意事項(xiàng),非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-11-11在PyCharm的 Terminal(終端)切換Python版本的方法
這篇文章主要介紹了在PyCharm的 Terminal(終端)切換Python版本的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08python3實(shí)現(xiàn)爬取淘寶美食代碼分享
本文給大家分享的是如何使用python3來爬取淘寶美食圖片標(biāo)題等信息的方法和代碼,有需要的小伙伴可以參考下2018-09-09python/Matplotlib繪制復(fù)變函數(shù)圖像教程
今天小編就為大家分享一篇python/Matplotlib繪制復(fù)變函數(shù)圖像教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11