詳解Python中contextlib上下文管理模塊的用法
咱們用的os模塊,讀取文件的時(shí)候,其實(shí)他是含有__enter__ __exit__ 。 一個(gè)是with觸發(fā)的時(shí)候,一個(gè)是退出的時(shí)候。
with file('nima,'r') as f:
print f.readline()
那咱們自己再實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的可以with的類。 我個(gè)人寫(xiě)python的時(shí)候,喜歡針對(duì)一些需要有關(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是個(gè)比with優(yōu)美的東西,也是提供上下文機(jī)制的模塊,它是通過(guò)Generator裝飾器實(shí)現(xiàn)的,不再是采用__enter__和__exit__。contextlib中的contextmanager作為裝飾器來(lái)提供一種針對(duì)函數(shù)級(jí)別的上下文管理機(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
我這里再貼下我上次寫(xiě)的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
一個(gè)上下文管理器通過(guò)with聲明激活, 而且API包含兩個(gè)方法。__enter__()方法運(yùn)行執(zhí)行流進(jìn)入到with代碼塊內(nèi)。他返回一個(gè)對(duì)象共上下文使用。當(dāng)執(zhí)行流離開(kāi)with塊時(shí),__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í)行上下文管理器時(shí)會(huì)調(diào)用__enter__離開(kāi)時(shí)調(diào)用__exit__。
__enter__能返回任意對(duì)象,聯(lián)合一個(gè)指定名字于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)該返回一個(gè)True值表明這個(gè)異常不需要傳播,返回False異常會(huì)在執(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)方法,通過(guò)編寫(xiě)一個(gè)類與__enter__()和__exit__()方法,并不困難。但有時(shí)比你需要的開(kāi)銷只是管理一個(gè)微不足道的上下文。在這類情況下,您可以使用contextmanager() decorat or 生成器函數(shù)轉(zhuǎn)換成一個(gè)上下文管理器。
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()可以同時(shí)管理多個(gè)上下文。
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的句柄
文件類支持上下文管理器, 但是有一些對(duì)象不支持。還有一些類使用close()方法但是不支持上下文管理器。我們使用closing()來(lái)為他創(chuàng)建一個(gè)上下文管理器。(類必須有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 通過(guò)類中一個(gè)方法獲取另一個(gè)方法變量的實(shí)例
今天小編就為大家分享一篇python 通過(guò)類中一個(gè)方法獲取另一個(gè)方法變量的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
Python2和Python3中urllib庫(kù)中urlencode的使用注意事項(xiàng)
這篇文章主要介紹了Python2和Python3中urllib庫(kù)中urlencode的使用注意事項(xiàng),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11
在PyCharm的 Terminal(終端)切換Python版本的方法
這篇文章主要介紹了在PyCharm的 Terminal(終端)切換Python版本的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Python出現(xiàn)segfault錯(cuò)誤解決方法
這篇文章主要介紹了Python出現(xiàn)segfault錯(cuò)誤解決方法,分析了系統(tǒng)日志提示segfault錯(cuò)誤的原因與對(duì)應(yīng)的解決方法,需要的朋友可以參考下2016-04-04
python3實(shí)現(xiàn)爬取淘寶美食代碼分享
本文給大家分享的是如何使用python3來(lái)爬取淘寶美食圖片標(biāo)題等信息的方法和代碼,有需要的小伙伴可以參考下2018-09-09
python/Matplotlib繪制復(fù)變函數(shù)圖像教程
今天小編就為大家分享一篇python/Matplotlib繪制復(fù)變函數(shù)圖像教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11

