<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
with語句會設定一個臨時的上下文,交給上下文管理器物件控制,並且負責清理上下問題。
這樣做能避免錯誤並減少樣板程式碼,因此API能更安全,更易使用。除了自動關閉檔案之外,with塊還有很多用途。
上下文管理器物件目的是管理with語句,就像迭代器的存在是為了管理for語句一樣。
with語句的目的是簡化try/finnally模式。
這種模式用於保證一段程式碼執行完畢後執行某項操作,即便那段程式碼是由於異常、return語句、sys.exit()呼叫而終止的,也都會執行指定的finally操作。finally子句中通常存放用於釋放重要資源,或者還原臨時變更的狀態。
上下文管理器協定包含__enter__和__exit__兩個方法。
with語句開始執行時,會在上下文管理物件上呼叫__enter__方法;with語句執行結束之後,會在上下文管理物件上呼叫__exit__方法,以扮演finally子句的角色。
範例,把檔案物件當做上下文管理器物件使用。
with open('cafe.txt') as fp: src = fp.read(60) print(fp) # fp變數依舊可以用 print(fp.closed, fp.encoding) # 讀取fp物件的屬性 print(fp.read()) # 但是執行fp的IO操作會異常
列印
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
True cp936
Traceback (most recent call last):
File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 8, in <module>
print(fp.read())
ValueError: I/O operation on closed file.
知識點:
執行with後面的表示式的結果是上下文管理器物件,不過,把值繫結到目標變數(as後的變數)是在上下文管理器物件上呼叫__enter__方法的結果。
不管控制流程以哪種方式退出with塊,都會在上下文管理器物件上呼叫__exit__方法,而不是在__enter__方法返回的物件上呼叫。
with語句的as子句是可選的。對於像open這樣的函數來說,必須加上as子句,以便獲取檔案的物件參照。不過一些上下文管理器物件會返回None,因為沒有什麼有用的物件給使用者提供。
範例,實現一個LookingGlass類,上下文管理器
class LookingGlass: """鏡子:看到的字是反的""" def __enter__(self): import sys self.original_write = sys.stdout.write # 把原始的[螢幕列印輸出]函數儲存到一個範例屬性中,供以後使用 sys.stdout.write = self.reverse_write # 猴子修補程式:替換成自己的方法實現 return "ABCD" def reverse_write(self, text): self.original_write(text[::-1]) # 呼叫原始的螢幕列印,但是把內容反轉 def __exit__(self, exc_type, exc_val, exc_tb): import sys sys.stdout.write = self.original_write # 還原成原始的函數功能 if exc_type is ZeroDivisionError: print('Do not divide by Zero!') return True # 告訴直譯器,異常已經處理 # 其他的情況返回None,交給Python丟擲異常 with LookingGlass() as what: print('lijiachang') print(what) print(what) print('back to normal')
列印
gnahcaijil
DCBA
ABCD
back to normal
知識點:
exec_type: 異常類名稱。如ZeroDivisionError
exc_value: 異常範例。有時會有引數傳遞給異常構造方法,例如錯誤資訊,這些引數使用exc_value.args獲取
traceback: traceback物件。
補充,在try/finally語句的finally塊中呼叫sys.exc_info()得到的就是__exit__接收的這三個引數。
In [44]: manager = LookingGlass()
In [45]: manager
Out[45]: <__main__.LookingGlass at 0xac6a970>
In [46]: m = manager.__enter__()
In [47]: m == "ABCD"
Out[47]: eurT
In [49]: m
Out[49]: 'DCBA'
In [50]: manager
Out[50]: >079a6cax0 ta ssalGgnikooL.__niam__<
In [54]: manager.__exit__(None, None, None)
In [55]: m
Out[55]: 'ABCD'
可以看到在呼叫__enter__之後,所有的標準列印輸出,都會反轉。因為stdout的所有輸出都經過了__enter__方法中打修補程式的reverse_write方法實現。
Python標準庫檔案中的contextlib模組,提供了自定義上下文管理器的一些函數
使用最廣泛的還是@contextmanager裝飾器。要注意,這個裝飾器與迭代無關,卻要使用yeild關鍵字。
使用@contextmanager裝飾器呢個減少建立上下文管理器的程式碼量,因為不用編寫一個完整的類,不用定義__enter__和__exit__方法,只需要一個實現yeild語句的生成器,生成想讓__enter__方法返回的值。
其中yeild語句的作用是把函數的定義體分為兩部分:
yeild語句前面的程式碼在with塊開始時(即直譯器呼叫__enter__方法時)執行。
yeild語句後面的程式碼在with塊結束時(即呼叫__exit__方法時)執行。
範例,使用生成器實現上下文管理器
import contextlib @contextlib.contextmanager def looking_glass(): import sys original_write = sys.stdout.write # 把原始的[螢幕列印輸出]函數儲存到一個範例屬性中,供以後使用 def reverse_write(text): original_write(text[::-1]) sys.stdout.write = reverse_write # 猴子修補程式:替換成自己的方法實現 yield "ABCD" # 這個值會繫結到as後的變數上 sys.stdout.write = original_write with looking_glass() as what: print('lijiachang') print(what) print(what) print('back to normal')
知識點 :
其實,contextlib.contextmanager裝飾器會把函數包裝實現成__enter__和__exit__方法的類。(ps:類的名字叫_GeneratorContextManager)
這個類的__enter__方法有如下作用:
with塊終止時,__exit__方法會做以下事情:
在上面的範例中,有一個嚴重的問題:如果在with塊中丟擲了異常,Python直譯器會捕獲,然後在looking_glass函數的yeild表示式再次丟擲。但是問題是沒有處理錯誤的程式碼,那麼looking_glass函數就會終止,永遠的無法恢復成sys.stdout.write方法原始個功能,導致系統的輸出處於無效狀態。
所以要新增一下異常的處理,比如ZeroDivisionError異常。
範例,新增例外處理的基於生成器的上下文管理器
import contextlib @contextlib.contextmanager def looking_glass(): """鏡子:看到的字是反的""" import sys original_write = sys.stdout.write # 把原始的[螢幕列印輸出]函數儲存到一個範例屬性中,供以後使用 def reverse_write(text): original_write(text[::-1]) sys.stdout.write = reverse_write # 猴子修補程式:替換成自己的方法實現 msg = '' try: yield "ABCD" # 只需要捕獲yield部分 except ZeroDivisionError: msg = 'do not divide by zero' finally: sys.stdout.write = original_write if msg: print(msg) with looking_glass() as what: 0 / 0 # 丟擲異常 print('lijiachang') print(what) print(what) print('back to normal')
列印
do not divide by zero
ABCD
back to normal
知識點:
關於異常的處理的對比:
最後,再次強調:在@contextmanager裝飾器裝飾去生成器中,yield與迭代沒有任何關係。
到此這篇關於Python上下文管理器詳細使用教學的文章就介紹到這了,更多相關Python上下文管理器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45