詳解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的類(lèi)。 我個(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è)類(lèi)與__enter__()和__exit__()方法,并不困難。但有時(shí)比你需要的開(kāi)銷(xiāo)只是管理一個(gè)微不足道的上下文。在這類(lèi)情況下,您可以使用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的句柄
文件類(lèi)支持上下文管理器, 但是有一些對(duì)象不支持。還有一些類(lèi)使用close()方法但是不支持上下文管理器。我們使用closing()來(lái)為他創(chuàng)建一個(gè)上下文管理器。(類(lèi)必須有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)文章
typing.Dict和Dict的區(qū)別及它們?cè)赑ython中的用途小結(jié)
當(dāng)在 Python 函數(shù)中聲明一個(gè) dictionary 作為參數(shù)時(shí),我們一般會(huì)把 key 和 value 的數(shù)據(jù)類(lèi)型聲明為全局變量,而不是局部變量。,這篇文章主要介紹了typing.Dict和Dict的區(qū)別及它們?cè)赑ython中的用途小結(jié),需要的朋友可以參考下2023-06-06
Pygame改編飛機(jī)大戰(zhàn)制作兔子接月餅游戲
一年中秋又快到了,今年加入了Python的學(xué)習(xí)行列,得益于Python的開(kāi)發(fā)效率和易讀性,網(wǎng)上寫(xiě)文章的次數(shù)多了起來(lái),既然是中秋節(jié)那肯定要搞個(gè)應(yīng)景的游戲才行2022-09-09
Python3如何使用range函數(shù)替代xrange函數(shù)
這篇文章主要介紹了Python3如何使用range函數(shù)替代xrange函數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
詳解Python如何巧妙實(shí)現(xiàn)數(shù)學(xué)階乘n!
一個(gè)正整數(shù)的階乘(factorial)是所有小于及等于該數(shù)的正整數(shù)的積,并且0的階乘為1。自然數(shù)n的階乘寫(xiě)作n!,本文就給大家介紹如何使用python和第三方庫(kù)來(lái)實(shí)現(xiàn)數(shù)學(xué)運(yùn)算中的階乘以及階乘累計(jì)求和2023-03-03
Python統(tǒng)計(jì)不同整數(shù)的出現(xiàn)次數(shù)的三種方法
在數(shù)據(jù)處理和分析領(lǐng)域,統(tǒng)計(jì)不同整數(shù)的出現(xiàn)次數(shù)是一個(gè)常見(jiàn)需求,無(wú)論是清洗日志數(shù)據(jù)、分析用戶(hù)行為,還是處理實(shí)驗(yàn)觀測(cè)值,我們都需要快速準(zhǔn)確地獲取不同整數(shù)的數(shù)量,本文將用通俗的語(yǔ)言,結(jié)合Python代碼示例,為你系統(tǒng)講解三種主流解決方案,需要的朋友可以參考下2025-04-04
Python被遠(yuǎn)程主機(jī)強(qiáng)制關(guān)閉后自動(dòng)重新運(yùn)行進(jìn)程的示例
要實(shí)現(xiàn)Python程序在被遠(yuǎn)程主機(jī)強(qiáng)制關(guān)閉后能夠自動(dòng)重新運(yùn)行,我們可以采用幾種方法,但最直接且常用的方法之一是結(jié)合操作系統(tǒng)級(jí)的工具或腳本,這篇文章主要介紹了Python被遠(yuǎn)程主機(jī)強(qiáng)制關(guān)閉后怎么自動(dòng)重新運(yùn)行進(jìn)程,需要的朋友可以參考下2024-08-08
Python操作數(shù)據(jù)庫(kù)之?dāng)?shù)據(jù)庫(kù)編程接口
這篇文章主要介紹了Python操作數(shù)據(jù)庫(kù)之?dāng)?shù)據(jù)庫(kù)編程接口,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-06-06
詳解Python?Selenium如何獲取鼠標(biāo)指向的元素
這篇文章主要介紹了如何通過(guò)Selenium獲取當(dāng)前鼠標(biāo)指向的元素,本文方法的核心,是借助JavaScript的事件(event)來(lái)獲取鼠標(biāo)所在的元素,感興趣的可以試一試2022-03-03
Python守護(hù)進(jìn)程用法實(shí)例分析
這篇文章主要介紹了Python守護(hù)進(jìn)程用法,實(shí)例分析了Python守護(hù)進(jìn)程的功能及使用方法,需要的朋友可以參考下2015-06-06

