python with语句与contextlib
with語句用于異常處理,適用于存在資源訪問的場合,無論在資源訪問的過程中是否發生異常,都會執行必要的清理操作,釋放資源,比如文件打開后自動關閉、線程中鎖的自動獲取和釋放
要明白with語句是怎樣使用的,有必要引入一些上下文管理器的相關概念
上下文管理器、上下文管理協議、運行時上下文、上下文表達式、語句體
上下文管理器和上下文管理協議是兩個相互關聯的定義:具體來說,上下文管理協議是指兩個方法:__enter__()和__exit__(),支持該協議的對象必須實現這兩個方法。而上下文管理器就是指支持上下文管理協議的對象。毫無疑問,這個對象實現了__enter__()、__exit__()兩個方法。
上下文管理器對象定義with語句運行時要建立的“運行時上下文”,負責with語句中上下文的進入和退出。通常使用with語句調用上下文管理器,也可以直接調用其方法來實現。
運行時上下文:__enter__()會在語句體執行之前進入“運行時上下文”,__exit__()會在語句體執行完畢后從運行時上下文退出
上下文表達式:跟在with后面的語句,是用來返回一個上下文管理器的
語句體:with語句中包裹的部分
with語句的語法格式:
with context_expression [as target(s)]:with-body這里 context_expression 要返回一個上下文管理器對象,該對象并不賦值給 as 子句中的 target(s) ,如果指定了 as 子句的話,會將上下文管理器的 __enter__()方法的返回值賦值給 target(s)。
target(s) 可以是單個變量,或者由“()”括起來的元組(不能是僅僅由“,”分隔的變量列表,必須加“()”)。
Python 對一些內建對象進行改進,加入了對上下文管理器的支持,可以用于 with 語句中,比如可以自動關閉文件、線程鎖的自動獲取和釋放等。假設要對一個文件進行操作,使用 with 語句可以有如下代碼:
with open(r'somefileName') as somefile:for line in somefile:print line# ...more code這里使用了 with 語句,不管在處理文件過程中是否發生異常,都能保證 with 語句執行完畢后已經關閉了打開的文件句柄。如果使用傳統的 try/finally 范式,則要使用類似如下代碼:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' somefile = open(r'somefileName') try:for line in somefile:print line# ...more code finally:somefile.close()比較起來,使用 with 語句可以減少編碼量。已經加入對上下文管理協議支持的還有模塊 threading、decimal 等。
contextlib
我們已經知道,要讓一個對象用于with語句,就必須實現上下文管理,而實現上下文管理是通過__enter__和__exit__這兩個方法實現的:
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的內建模塊contextlib能讓我們的實現變得更簡便
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' 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,這個裝飾器接受一個generator,在生成器中用yield語句將想要用于with語句的變量輸出出去。
加入我們想要在每次運行代碼的前后都運行特定的代碼,我們也可以選用@contextmanger這個裝飾器實現
@contextmanager def tag(name):print("<%s>" % name)yield#這個yield調用會執行with語句中的所有語句,因此with語句中的所有內容都將會被運行print("</%s>" % name)with tag("h1"):print("hello")print("world") #這段代碼的執行效果是: <h1> hello world </h1>@closing
此外,如果一個對象沒有實現運行時上下文,他就不能被應用到with語句當中,我們可以使用contextlib提供的@closing裝飾器將其變為上下文對象
from contextlib import closing from urllib.request import urlopenwith closing(urlopen('https://www.python.org')) as page:for line in page:print(line)closing其實也是一個經過@contextmanger裝飾的genterator,
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' @contextmanager def closing(thing):try:yield thingfinally:thing.close()它的作用就是將任意對象變為上下文對象,并支持with語句
奇怪的是,并不是所有的對象都能夠通過closing()方法變為上下文對象:(錯誤是Query對象沒有實現close()方法)
class Query(object):def __init__(self,name):self.name=name ...with closing(Query('bob')) as p:print(p.name) #錯誤信息: 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>總結
以上是生活随笔為你收集整理的python with语句与contextlib的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python itertools 操作迭
- 下一篇: Python线程join和setDaem