python with语句与contextlib
with語(yǔ)句用于異常處理,適用于存在資源訪問(wèn)的場(chǎng)合,無(wú)論在資源訪問(wèn)的過(guò)程中是否發(fā)生異常,都會(huì)執(zhí)行必要的清理操作,釋放資源,比如文件打開(kāi)后自動(dòng)關(guān)閉、線程中鎖的自動(dòng)獲取和釋放
要明白with語(yǔ)句是怎樣使用的,有必要引入一些上下文管理器的相關(guān)概念
上下文管理器、上下文管理協(xié)議、運(yùn)行時(shí)上下文、上下文表達(dá)式、語(yǔ)句體
上下文管理器和上下文管理協(xié)議是兩個(gè)相互關(guān)聯(lián)的定義:具體來(lái)說(shuō),上下文管理協(xié)議是指兩個(gè)方法:__enter__()和__exit__(),支持該協(xié)議的對(duì)象必須實(shí)現(xiàn)這兩個(gè)方法。而上下文管理器就是指支持上下文管理協(xié)議的對(duì)象。毫無(wú)疑問(wèn),這個(gè)對(duì)象實(shí)現(xiàn)了__enter__()、__exit__()兩個(gè)方法。
上下文管理器對(duì)象定義with語(yǔ)句運(yùn)行時(shí)要建立的“運(yùn)行時(shí)上下文”,負(fù)責(zé)with語(yǔ)句中上下文的進(jìn)入和退出。通常使用with語(yǔ)句調(diào)用上下文管理器,也可以直接調(diào)用其方法來(lái)實(shí)現(xiàn)。
運(yùn)行時(shí)上下文:__enter__()會(huì)在語(yǔ)句體執(zhí)行之前進(jìn)入“運(yùn)行時(shí)上下文”,__exit__()會(huì)在語(yǔ)句體執(zhí)行完畢后從運(yùn)行時(shí)上下文退出
上下文表達(dá)式:跟在with后面的語(yǔ)句,是用來(lái)返回一個(gè)上下文管理器的
語(yǔ)句體:with語(yǔ)句中包裹的部分
with語(yǔ)句的語(yǔ)法格式:
with context_expression [as target(s)]:with-body這里 context_expression 要返回一個(gè)上下文管理器對(duì)象,該對(duì)象并不賦值給 as 子句中的 target(s) ,如果指定了 as 子句的話(huà),會(huì)將上下文管理器的 __enter__()方法的返回值賦值給 target(s)。
target(s) 可以是單個(gè)變量,或者由“()”括起來(lái)的元組(不能是僅僅由“,”分隔的變量列表,必須加“()”)。
Python 對(duì)一些內(nèi)建對(duì)象進(jìn)行改進(jìn),加入了對(duì)上下文管理器的支持,可以用于 with 語(yǔ)句中,比如可以自動(dòng)關(guān)閉文件、線程鎖的自動(dòng)獲取和釋放等。假設(shè)要對(duì)一個(gè)文件進(jìn)行操作,使用 with 語(yǔ)句可以有如下代碼:
with open(r'somefileName') as somefile:for line in somefile:print line# ...more code這里使用了 with 語(yǔ)句,不管在處理文件過(guò)程中是否發(fā)生異常,都能保證 with 語(yǔ)句執(zhí)行完畢后已經(jīng)關(guān)閉了打開(kāi)的文件句柄。如果使用傳統(tǒng)的 try/finally 范式,則要使用類(lèi)似如下代碼:
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' somefile = open(r'somefileName') try:for line in somefile:print line# ...more code finally:somefile.close()比較起來(lái),使用 with 語(yǔ)句可以減少編碼量。已經(jīng)加入對(duì)上下文管理協(xié)議支持的還有模塊 threading、decimal 等。
contextlib
我們已經(jīng)知道,要讓一個(gè)對(duì)象用于with語(yǔ)句,就必須實(shí)現(xiàn)上下文管理,而實(shí)現(xiàn)上下文管理是通過(guò)__enter__和__exit__這兩個(gè)方法實(shí)現(xiàn)的:
class Query(object):def __init__(self, name):self.name = namedef __enter__(self):print('Begin')return selfdef __exit__(self, exc_type, exc_value, traceback):if exc_type:print('Error')else:print('End')def query(self):with Query('Bob') as q:q.query()但是這樣有些麻煩,Python的內(nèi)建模塊contextlib能讓我們的實(shí)現(xiàn)變得更簡(jiǎn)便
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' from contextlib import contextmanagerclass Query(object):def __init__(self, name):self.name = namedef query(self):print('Query info about %s...' % self.name)@contextmanager def create_query(name):print('Begin')q = Query(name)yield qprint('End')with create_query('Bob') as q:q.query()contextlib提供的裝飾器@contextmanger,這個(gè)裝飾器接受一個(gè)generator,在生成器中用yield語(yǔ)句將想要用于with語(yǔ)句的變量輸出出去。
加入我們想要在每次運(yùn)行代碼的前后都運(yùn)行特定的代碼,我們也可以選用@contextmanger這個(gè)裝飾器實(shí)現(xiàn)
@contextmanager def tag(name):print("<%s>" % name)yield#這個(gè)yield調(diào)用會(huì)執(zhí)行with語(yǔ)句中的所有語(yǔ)句,因此with語(yǔ)句中的所有內(nèi)容都將會(huì)被運(yùn)行print("</%s>" % name)with tag("h1"):print("hello")print("world") #這段代碼的執(zhí)行效果是: <h1> hello world </h1>@closing
此外,如果一個(gè)對(duì)象沒(méi)有實(shí)現(xiàn)運(yùn)行時(shí)上下文,他就不能被應(yīng)用到with語(yǔ)句當(dāng)中,我們可以使用contextlib提供的@closing裝飾器將其變?yōu)樯舷挛膶?duì)象
from contextlib import closing from urllib.request import urlopenwith closing(urlopen('https://www.python.org')) as page:for line in page:print(line)closing其實(shí)也是一個(gè)經(jīng)過(guò)@contextmanger裝飾的genterator,
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' @contextmanager def closing(thing):try:yield thingfinally:thing.close()它的作用就是將任意對(duì)象變?yōu)樯舷挛膶?duì)象,并支持with語(yǔ)句
奇怪的是,并不是所有的對(duì)象都能夠通過(guò)closing()方法變?yōu)樯舷挛膶?duì)象:(錯(cuò)誤是Query對(duì)象沒(méi)有實(shí)現(xiàn)close()方法)
class Query(object):def __init__(self,name):self.name=name ...with closing(Query('bob')) as p:print(p.name) #錯(cuò)誤信息: bob#仍然能輸出 Traceback (most recent call last):File "c:\Users\Administrator.SC-201605202132\.vscode\extensions\ms-python.python-2019.9.34911\pythonFiles\ptvsd_launcher.py", line 43, in <module>main(ptvsdArgs)File "c:\Users\Administrator.SC-201605202132\.vscode\extensions\ms-python.python-2019.9.34911\pythonFiles\lib\python\ptvsd\__main__.py", line 432, in mainrun()File "c:\Users\Administrator.SC-201605202132\.vscode\extensions\ms-python.python-2019.9.34911\pythonFiles\lib\python\ptvsd\__main__.py", line 316, in run_filerunpy.run_path(target, run_name='__main__')File "c:\users\administrator.sc-201605202132\appdata\local\programs\python\python36\Lib\runpy.py", line 263, in run_pathpkg_name=pkg_name, script_name=fname)File "c:\users\administrator.sc-201605202132\appdata\local\programs\python\python36\Lib\runpy.py", line 96, in _run_module_codemod_name, mod_spec, pkg_name, script_name)File "c:\users\administrator.sc-201605202132\appdata\local\programs\python\python36\Lib\runpy.py", line 85, in _run_codeexec(code, run_globals)File "c:\Users\Administrator.SC-201605202132\Envs\sort\app\forTest.py", line 26, in <module>print(p.name)File "c:\users\administrator.sc-201605202132\appdata\local\programs\python\python36\Lib\contextlib.py", line 185, in __exit__self.thing.close() AttributeError: 'Query' object has no attribute 'close' PS C:\Users\Administrator.SC-201605202132\Envs\sort>總結(jié)
以上是生活随笔為你收集整理的python with语句与contextlib的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python itertools 操作迭
- 下一篇: Python线程join和setDaem