正確理解python中的關(guān)鍵字“with”與上下文管理器
前言
如果你有閱讀源碼的習(xí)慣,可能會(huì)看到一些優(yōu)秀的代碼經(jīng)常出現(xiàn)帶有 “with” 關(guān)鍵字的語句,它通常用在什么場景呢?今天就來說說 with 和 上下文管理器。
對于系統(tǒng)資源如文件、數(shù)據(jù)庫連接、socket 而言,應(yīng)用程序打開這些資源并執(zhí)行完業(yè)務(wù)邏輯之后,必須做的一件事就是要關(guān)閉(斷開)該資源。
比如 Python 程序打開一個(gè)文件,往文件中寫內(nèi)容,寫完之后,就要關(guān)閉該文件,否則會(huì)出現(xiàn)什么情況呢?極端情況下會(huì)出現(xiàn) "Too many open files" 的錯(cuò)誤,因?yàn)橄到y(tǒng)允許你打開的最大文件數(shù)量是有限的。
同樣,對于數(shù)據(jù)庫,如果連接數(shù)過多而沒有及時(shí)關(guān)閉的話,就可能會(huì)出現(xiàn) "Can not connect to MySQL server Too many connections",因?yàn)閿?shù)據(jù)庫連接是一種非常昂貴的資源,不可能無限制的被創(chuàng)建。
來看看如何正確關(guān)閉一個(gè)文件。
普通版:
def m1():
f = open("output.txt", "w")
f.write("python之禪")
f.close()
這樣寫有一個(gè)潛在的問題,如果在調(diào)用 write 的過程中,出現(xiàn)了異常進(jìn)而導(dǎo)致后續(xù)代碼無法繼續(xù)執(zhí)行,close 方法無法被正常調(diào)用,因此資源就會(huì)一直被該程序占用者釋放。那么該如何改進(jìn)代碼呢?
進(jìn)階版:
def m2():
f = open("output.txt", "w")
try:
f.write("python之禪")
except IOError:
print("oops error")
finally:
f.close()
改良版本的程序是對可能發(fā)生異常的代碼處進(jìn)行 try 捕獲,使用 try/finally 語句,該語句表示如果在 try 代碼塊中程序出現(xiàn)了異常,后續(xù)代碼就不再執(zhí)行,而直接跳轉(zhuǎn)到 except 代碼塊。而無論如何,finally 塊的代碼最終都會(huì)被執(zhí)行。因此,只要把 close 放在 finally 代碼中,文件就一定會(huì)關(guān)閉。
高級(jí)版:
def m3():
with open("output.txt", "w") as f:
f.write("Python之禪")
一種更加簡潔、優(yōu)雅的方式就是用 with 關(guān)鍵字。open 方法的返回值賦值給變量 f,當(dāng)離開 with 代碼塊的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用 f.close() 方法, with 的作用和使用 try/finally 語句是一樣的。那么它的實(shí)現(xiàn)原理是什么?
在講 with 的原理前要涉及到另外一個(gè)概念,就是上下文管理器(Context Manager)。
上下文管理器
任何實(shí)現(xiàn)了 __enter__() 和 __exit__() 方法的對象都可稱之為上下文管理器,上下文管理器對象可以使用 with 關(guān)鍵字。顯然,文件(file)對象也實(shí)現(xiàn)了上下文管理器。
那么文件對象是如何實(shí)現(xiàn)這兩個(gè)方法的呢?我們可以模擬實(shí)現(xiàn)一個(gè)自己的文件類,讓該類實(shí)現(xiàn) __enter__() 和 __exit__() 方法。
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("will exit")
self.f.close()
__enter__() 方法返回資源對象,這里就是你將要打開的那個(gè)文件對象, __exit__() 方法處理一些清除工作。
因?yàn)?File 類實(shí)現(xiàn)了上下文管理器,現(xiàn)在就可以使用 with 語句了。
with File('out.txt', 'w') as f:
print("writing")
f.write('hello, python')
這樣,你就無需顯示地調(diào)用 close 方法了,由系統(tǒng)自動(dòng)去調(diào)用,哪怕中間遇到異常 close 方法也會(huì)被調(diào)用。
contextlib
Python 還提供了一個(gè) contextmanager 的裝飾器,更進(jìn)一步簡化了上下文管理器的實(shí)現(xiàn)方式。通過 yield 將函數(shù)分割成兩部分,yield 之前的語句在 __enter__ 方法中執(zhí)行,yield 之后的語句在 __exit__ 方法中執(zhí)行。緊跟在 yield 后面的值是函數(shù)的返回值。
from contextlib import contextmanager @contextmanager def my_open(path, mode): f = open(path, mode) yield f f.close()
調(diào)用
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
總結(jié)
Python 提供了 with 語法用于簡化資源操作的后續(xù)清除操作,是 try/finally 的替代方法,實(shí)現(xiàn)原理建立在上下文管理器之上。此外,Python 還提供了一個(gè) contextmanager 裝飾器,更進(jìn)一步簡化上下管理器的實(shí)現(xiàn)方式。以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
pyinstaller打包可執(zhí)行文件,存放路徑包含中文無法運(yùn)行的解決方案
這篇文章主要介紹了pyinstaller打包可執(zhí)行文件,存放路徑包含中文無法運(yùn)行的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
python flask框架實(shí)現(xiàn)重定向功能示例
這篇文章主要介紹了python flask框架實(shí)現(xiàn)重定向功能,結(jié)合實(shí)例形式分析了flask框架重定向功能的實(shí)現(xiàn)與使用方法,需要的朋友可以參考下2019-07-07
python實(shí)現(xiàn)的DES加密算法和3DES加密算法實(shí)例
這篇文章主要介紹了python實(shí)現(xiàn)的DES加密算法和3DES加密算法,以實(shí)例形式較為詳細(xì)的分析了DES加密算法和3DES加密算法的原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-06-06
TensorFlow 2.0之后動(dòng)態(tài)分配顯存方式
這篇文章主要介紹了TensorFlow 2.0之后動(dòng)態(tài)分配顯存方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Python數(shù)據(jù)分析與處理(一)--北京高考分?jǐn)?shù)線統(tǒng)計(jì)分析
這篇文章主要介紹了Python數(shù)據(jù)分析與處理北京高考分?jǐn)?shù)線統(tǒng)計(jì)分析,文章問繞Python數(shù)據(jù)分析與處理相關(guān)資料的介紹,展開對北京高考分?jǐn)?shù)線統(tǒng)計(jì)分析,需要的小伙伴可以參考一下2021-12-12
使用 python pyautogui實(shí)現(xiàn)鼠標(biāo)鍵盤控制功能
pyautogui是一個(gè)可以控制鼠標(biāo)和鍵盤的python庫,類似的還有pywin32。這篇文章主要介紹了python中的pyautogui實(shí)現(xiàn)鼠標(biāo)鍵盤控制功能,需要的朋友可以參考下2019-08-08
Python pycharm 同時(shí)加載多個(gè)項(xiàng)目的方法
今天小編就為大家分享一篇Python pycharm 同時(shí)加載多個(gè)項(xiàng)目的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01

