Python中的上下文管理器,實際上就是實現(xiàn)了上下文管理協(xié)議的對象。在Python中打開文件的時候,我們需要確保文件被使用完畢之后,對其進(jìn)行關(guān)閉操作——調(diào)用文件對象的close()方法。如果不使用上下文管理器,經(jīng)典的處理方式就是將close()方法的調(diào)用放在一個finally語句中:
f = open("www.log")
try:
print("do something with file")
finally:
f.close()
?這里finally的唯一作用就是確保文件對象最后被正確關(guān)閉,為此我們使用了一個try...finally語句塊——這就需要我們在實現(xiàn)業(yè)務(wù)邏輯的時候,去考慮一些業(yè)務(wù)邏輯之外的東西,我們需要一個類似于自動內(nèi)存回收(GC)的機制,來自動地幫我們做文件描述符的清理,讓我們把精力都用在業(yè)務(wù)邏輯實現(xiàn)上。上下文管理器就可以達(dá)到這樣的目的。而Python的文件對象是實現(xiàn)了這個協(xié)議了的:
with open("www.log") as f:
print("do something with file")
這和上面的try...finally風(fēng)格的代碼實現(xiàn)了等價的功能,只不過這里的代碼更加簡潔了,文件對象最后也可以被關(guān)閉。
上下文管理協(xié)議的定義非常簡單,就是在被上下文包覆的代碼執(zhí)行前執(zhí)行一個__enter__方法;在被包覆代碼之后執(zhí)行一個__exit__方法,根據(jù)這個定義我們就能寫出一個非常簡單的上下文管理器:
class SimpleContext:
def __enter__(self):
print("before code block")
def __exit__(self, exc_type, exc_val, exc_tb):
print("after code block")
if __name__ == '__main__':
with SimpleContext():
print("I am code block")
運行這個代碼的輸出為:
before code block
I am code block
after code block
可以看到,with塊下的代碼執(zhí)行之前,運行了上下文協(xié)議的__enter__方法,with塊下的代碼執(zhí)行之后,運行了上下文協(xié)議的__exit__ 方法。
可以通過在__enter__方法中返回一個值,將此值定義應(yīng)用在with塊中,可以通過as關(guān)鍵詞來獲取對這個值的引用,這就是上面看到的with open("www.log") as f中獲取對f引用的方法:
class SimpleContext:
def __enter__(self):
print("before code block")
return 100
def __exit__(self, exc_type, exc_val, exc_tb):
print("after code block")
if __name__ == '__main__':
with SimpleContext() as sc:
print(sc)
print("I am code block")
這樣輸出就變成了:
before code block
100
I am code block
after code block
另外可以通過__exit__方法的回調(diào)參數(shù)exc_type, exc_val, exc_tb來訪問with語句塊的代碼執(zhí)行的異常信息,如果代碼塊一切正常,它們都是None;如果有異常拋出,就可以通過這三個參數(shù)來獲取異常信息,做相應(yīng)的處理。
以上就是上下文管理器的一個完整的內(nèi)容了。但是Python中,為了讓實現(xiàn)上下文管理器的過程更加簡單,提供了另一種實現(xiàn)上下文協(xié)議的方法,就是通過contextlib模塊中的contextmanager裝飾器,結(jié)合使用生成器來實現(xiàn):
import contextlib
@contextlib.contextmanager
def SimpleContext():
print("before code block")
try:
yield 100
except Exception as e:
print("handle e here, not in __exit__")
finally:
print("after code block")
if __name__ == '__main__':
with SimpleContext() as sc:
print(sc)
print("I am code block")
這個實現(xiàn)的輸出和上面的輸出是一致的,只是寫法的不同。第一種寫法結(jié)構(gòu)上更加清晰,第二種寫法邏輯上更加連續(xù),哪一種方法更好,作出自己的選擇即可。
以上。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
