您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“python中with ... as語句的詳細講解”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
說到 with 大家通??吹降膽撌沁@樣的:
示例 1
with open('courses.txt') as f:
for i in f:
print(i.strip())
打開一個文件,然后循環(huán)做一些事情。但是你知道為什么會有 with 嗎?我們自己是不是能夠?qū)懗隹梢宰饔迷?with 關(guān)鍵字上的對象呢?
現(xiàn)在,我們帶著上述兩個問題來說一說 with 的由來以及上下文管理器相關(guān)內(nèi)容。
with 語句的目的是簡化 try/finally 模式。這種模式用于保證一段代碼運行完畢后執(zhí)行某項操作,即便那段代碼由于異常、return 語句或sys.exit() 調(diào)用而中止,也會執(zhí)行指定的操作。finally 子句中的代碼通常用于釋放重要的資源,或者還原臨時變更的狀態(tài)。
示例1的功能我們可以使用 try/finally 模式實現(xiàn):
示例2
try:
f = open('courses.txt')
for i in f:
print(i.strip())
finally:
f.close()
try中的 except 和 else 不是必須的,這里為了簡單明了,我們只用了 finally。
對比兩個示例,我們可以看到示例1相對簡潔,這就是 with 的由來。
其實,語言中的一些特性或者說一些亮眼的特性,必然是有一個演化的過程的,我們作為后來者和使用者應該多花一些心思去思索其背后的實現(xiàn)過程,相信你會收獲更多。
那么 上下文管理器 又是什么呢?
上下文管理器協(xié)議包含 __enter__ 和 __exit__ 兩個方法。with 語句開始運行時,會在上下文管理器對象上調(diào)用 __enter__ 方法。with 語句運行結(jié)束后,會在上下文管理器對象上調(diào)用 __exit__ 方法,以此扮演 finally 子句的角色。最常見的例子是確保關(guān)閉文件對象(示例1)。
下面我們實現(xiàn)一個簡單的例子來直觀的感受一下:
class T:
def __enter__(self):
print('T.__enter__')
return '我是__enter__的返回值'
def __exit__(self, exc_type, exc_val, exc_tb):
print('T.__exit__')
with T() as t:
print(t)
輸出:
T.__enter__ 我是__enter__的返回值 T.__exit__
示例3中實現(xiàn)了一個類T,它的對象包含了__enter__和__exit__方法,有了這兩個方法就可以使用 with 處理該對象。執(zhí)行 with 后面的表達式T()得到的是上下文管理器對象,通過as字句把對象綁定到了變量t上。
觀察輸出結(jié)果,可以看到with塊先調(diào)用了__enter__方法,在處理完內(nèi)部邏輯(print(t))之后調(diào)用了exit方法,而t其實就是__enter__方法的返回值。
當然,這個例子只是為了方便我們理解上下文管理器,下面我們看一個更有意思的例子:
示例4
obj1 = HaHa('你手機拿反了')
with obj1 as content:
print('哈哈鏡花緣')
print(content)
print('#### with 執(zhí)行完畢后,再輸出content: ####')
print(content)
輸出:
緣花鏡哈哈 了反拿機手你 #### with 執(zhí)行完畢后,在輸出content: #### 你手機拿反了
示例4中,上下文管理器是 HaHa 類的實例,Python 調(diào)用此實例的 __enter__ 方法,把返回結(jié)果綁定到 變量content 上。
打印一個字符串,然后打印 content 變量的值??梢钥吹酱蛴〕龅膬?nèi)容都是是反向的。
最后,當 with 塊已經(jīng)執(zhí)行完畢。可以看出,__enter__ 方法返回的值——即存儲在 content 變量中的值——是字符串 ‘你手機拿反了’。
輸出不再是反向的了。
HaHa類的實現(xiàn):
import sys
class HaHa:
def __init__(self, word):
self.word = word
def reverse_write(self, text):
self.original_write(text[::-1])
def __enter__(self):
self.original_write = sys.stdout.write
sys.stdout.write = self.reverse_write
return self.word
def __exit__(self, exc_type, exc_value, traceback):
sys.stdout.write = self.original_write
return True
在__enter__方法中,我們接管了標準輸出,將其替換成我們自己編寫的方法reverse_write,reverse_write方法將參數(shù)內(nèi)容反轉(zhuǎn)。而在__exit__方法中,我們將標準輸出還原。__exit__方法需要返回True。
總之,with之于上下文管理器,就像for之于迭代器一樣。with就是為了方便上下文管理器的使用。
上下文管理器特性在標準庫中有一些應用:
在 sqlite3 模塊中用于管理事務;
在 threading 模塊中用于維護鎖、條件和信號;
另外,說到上下文管理器就不得不提一下@contextmanager 裝飾器,它能減少創(chuàng)建上下文管理器的樣板代碼量,因為不用編寫一個完整的類,定義 __enter__和 __exit__ 方法,而只需實現(xiàn)有一個 yield 語句的生成器,生成想讓 __enter__ 方法返回的值。
在使用 @contextmanager 裝飾的生成器中,yield 語句的作用是把函數(shù)的定義體分成兩部分:
yield 語句前面的所有代碼在 with 塊開始時(即解釋器調(diào)用 __enter__ 方法時)執(zhí)行
yield 語句后面的代碼在with 塊結(jié)束時(即調(diào)用 __exit__ 方法時)執(zhí)行。
下面我們用 @contextmanager 裝飾器來實現(xiàn)一下示例4的功能:
示例5
import sys
import contextlib
@contextlib.contextmanager
def WoHa(n):
original_write = sys.stdout.write
def reverse_write(text):
original_write(text[::-1])
sys.stdout.write = reverse_write
yield n
sys.stdout.write = original_write
return True
obj1 = WoHa('你手機拿反了')
with obj1 as content:
print('哈哈鏡花緣')
print(content)
print('#### with 執(zhí)行完畢后,在輸出content: ####')
print(content)
輸出:
緣花鏡哈哈
了反拿機手你
#### with 執(zhí)行完畢后,在輸出content: ####
你手機拿反了
這里我們需要注意的是:代碼執(zhí)行到y(tǒng)ield時,會產(chǎn)出一個值,這個值會綁定到 with 語句中 as 子句的變量上。執(zhí)行 with 塊中的代碼時,這個函數(shù)會在yield這里暫停。此時,相當于示例4中執(zhí)行完__enter__方法。而控制權(quán)一旦跳出 with 塊(塊內(nèi)代碼執(zhí)行完畢)則繼續(xù)執(zhí)行 yield 語句之后的代碼。
@contextmanager 裝飾器優(yōu)雅且實用,把三個不同的 Python 特性結(jié)合到了一起:函數(shù)裝飾器、生成器和 with 語句。
“python中with ... as語句的詳細講解”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。