Python中的with語句與上下文管理器學(xué)習(xí)總結(jié)
0、關(guān)于上下文管理器
上下文管理器是可以在with語句中使用,擁有__enter__和__exit__方法的對(duì)象。
with manager as var: do_something(var)
相當(dāng)于以下情況的簡(jiǎn)化:
var = manager.__enter__() try: do_something(var) finally: manager.__exit__()
換言之,PEP 343中定義的上下文管理器協(xié)議允許將無聊的try...except...finally結(jié)構(gòu)抽象到一個(gè)單獨(dú)的類中,僅僅留下關(guān)注的do_something部分。
__enter__方法首先被調(diào)用。它可以返回賦給var的值。as部分是可選的:如果它不出現(xiàn),enter的返回值簡(jiǎn)單地被忽略。
with語句下的代碼被執(zhí)行。就像try子句,它們或者成功執(zhí)行到底,或者break,continue或return,或者可以拋出異常。無論哪種情況,該塊結(jié)束后,__exit__方法被調(diào)用。如果拋出異常,異常信息被傳遞給__exit__,這將在下一章節(jié)討論。通常情況下,異??杀缓雎?,就像在finally子句中一樣,并且將在__exit__結(jié)束后重新拋出。
比如說我們想確認(rèn)一個(gè)文件在完成寫操作之后被立即關(guān)閉:
>>> class closing(object): ... def __init__(self, obj): ... self.obj = obj ... def __enter__(self): ... return self.obj ... def __exit__(self, *args): ... self.obj.close() >>> with closing(open('/tmp/file', 'w')) as f: ... f.write('the contents\n')
這里我們確保了當(dāng)with塊退出時(shí)調(diào)用了f.close()。因?yàn)殛P(guān)閉文件是非常常見的操作,該支持已經(jīng)出現(xiàn)在file類之中。它有一個(gè)__exit__方法調(diào)用close,并且本身可作為上下文管理器。
>>> with open('/tmp/file', 'a') as f: ... f.write('more contents\n')
try...finally常見的用法是釋放資源。各種不同的情況實(shí)現(xiàn)相似:在__enter__階段資源被獲得,在__exit__階段釋放,如果拋出異常也被傳遞。正如文件操作,往往這是對(duì)象使用后的自然操作,內(nèi)置支持使之很方便。每一個(gè)版本,Python都在更多的地方提供支持。
1、如何使用上下文管理器:
如何打開一個(gè)文件,并寫入"hello world"
filename="my.txt" mode="w" writer=open(filename,mode) writer.write("hello world") writer.close()
當(dāng)發(fā)生異常時(shí)(如磁盤寫滿),就沒有機(jī)會(huì)執(zhí)行第5行。當(dāng)然,我們可以采用try-finally語句塊進(jìn)行包裝:
writer=open(filename,mode) try: writer.write("hello world") finally: writer.close()
當(dāng)我們進(jìn)行復(fù)雜的操作時(shí),try-finally語句就會(huì)變得丑陋,采用with語句重寫:
with open(filename,mode) as writer: writer.write("hello world")
as指代了從open()函數(shù)返回的內(nèi)容,并把它賦給了新值。with完成了try-finally的任務(wù)。
2、自定義上下文管理器
with語句的作用類似于try-finally,提供一種上下文機(jī)制。要應(yīng)用with語句的類,其內(nèi)部必須提供兩個(gè)內(nèi)置函數(shù)__enter__和__exit__。前者在主體代碼執(zhí)行前執(zhí)行,后者在主體代碼執(zhí)行后執(zhí)行。as后面的變量,是在__enter__函數(shù)中返回的。
class echo(): def output(self): print "hello world" def __enter__(self): print "enter" return self #可以返回任何希望返回的東西 def __exit__(self,exception_type,value,trackback): print "exit" if exception_type==ValueError: return True else: return Flase >>>with echo as e: e.output()
輸出:
enter hello world exit
def __exit__(self,exc_type,exc_value,exc_tb)
其中,exc_type:異常類型;exc_value:異常值;exc_tb:異常追蹤信息
當(dāng)__exit__返回True時(shí),異常不傳播
3、contextlib模塊
contextlib模塊的作用是提供更易用的上下文管理器,它是通過Generator實(shí)現(xiàn)的。contextlib中的contextmanager作為裝飾器來提供一種針對(duì)函數(shù)級(jí)別的上下文管理機(jī)制,常用框架如下:
from contextlib import contextmanager @contextmanager def make_context(): print 'enter' try: yield "ok" except RuntimeError,err: print 'error',err finally: print 'exit' >>>with make_context() as value: print value
輸出為:
enter ok exit
其中,yield寫入try-finally中是為了保證異常安全(能處理異常)as后的變量的值是由yield返回。yield前面的語句可看作代碼塊執(zhí)行前操作,yield之后的操作可以看作在__exit__函數(shù)中的操作。
以線程鎖為例:
@contextlib.contextmanager def loudLock(): print 'Locking' lock.acquire() yield print 'Releasing' lock.release() with loudLock(): print 'Lock is locked: %s' % lock.locked() print 'Doing something that needs locking' #Output: #Locking #Lock is locked: True #Doing something that needs locking #Releasing
4、contextlib.nested:減少嵌套
對(duì)于:
with open(filename,mode) as reader: with open(filename1,mode1) as writer: writer.write(reader.read())
可以通過contextlib.nested進(jìn)行簡(jiǎn)化:
with contextlib.nested(open(filename,mode),open(filename1,mode1)) as (reader,writer): writer.write(reader.read())
在python 2.7及以后,被一種新的語法取代:
with open(filename,mode) as reader,open(filename1,mode1) as writer: writer.write(reader.read())
5、contextlib.closing()
file類直接支持上下文管理器API,但有些表示打開句柄的對(duì)象并不支持,如urllib.urlopen()返回的對(duì)象。還有些遺留類,使用close()方法而不支持上下文管理器API。為了確保關(guān)閉句柄,需要使用closing()為它創(chuàng)建一個(gè)上下文管理器(調(diào)用類的close方法)。
import contextlib class myclass(): def __init__(self): print '__init__' def close(self): print 'close()' with contextlib.closing(myclass()): print 'ok'
輸出:
__init__ ok close()
相關(guān)文章
Python+SQLAlchemy輕松實(shí)現(xiàn)管理數(shù)據(jù)庫
QLAlchemy是一個(gè)強(qiáng)大的ORM(對(duì)象關(guān)系映射)庫,它允許您通過Python代碼與關(guān)系型數(shù)據(jù)庫進(jìn)行交互,本文我們將學(xué)習(xí)如何使用Python和SQLAlchemy庫來輕松管理數(shù)據(jù)庫,需要的可以參考下2023-05-05pandas之分組groupby()的使用整理與總結(jié)
這篇文章主要介紹了pandas之分組groupby()的使用整理與總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06一篇文章帶你搞懂Python類的相關(guān)知識(shí)
今天我們要說的是面向?qū)ο蟮暮诵?----類,類能幫我們把復(fù)雜的事情變得有條理,有順序,希望大家通過學(xué)習(xí)類能改善自己的編碼風(fēng)格,使代碼變得更為好看,更加通俗易懂,需要的朋友可以參考下2021-05-05Django框架HttpResponse和HttpRequest對(duì)象學(xué)習(xí)
這篇文章主要介紹了Django框架HttpResponse和HttpRequest對(duì)象學(xué)習(xí),有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家早日升職加薪2021-09-09pytho多張圖片的無損拼接的實(shí)現(xiàn)示例
很多人都會(huì)是用PS進(jìn)行拼接,本文主要介紹了pytho多張圖片的無損拼接的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Python通過PIL獲取圖片主要顏色并和顏色庫進(jìn)行對(duì)比的方法
這篇文章主要介紹了Python通過PIL獲取圖片主要顏色并和顏色庫進(jìn)行對(duì)比的方法,實(shí)例分析了Python通過PIL模塊操作圖片的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03