詳細講解python中的關鍵字「with」與上下文管理器

Y2J
發布: 2017-05-02 16:04:23
原創
1425 人瀏覽過

這篇文章主要介紹了關於python中關鍵字"with"和上下文管理器的相關資料,文中介紹的非常詳細,相信對大家學習或使用python具有一定的參考價值,需要的朋友們下面來一起看看吧。

前言

如果你有閱讀原始碼的習慣,可能會看到一些優秀的程式碼經常出現有「with」關鍵字的語句,它通常用在什麼場景?今天就來談談 with 和 上下文管理器。

對於系統資源如檔案、資料庫連線、socket 而言,應用程式開啟這些資源並執行完業務邏輯之後,必須做的一件事就是要關閉(已中斷)該資源。

例如 Python 程式開啟一個文件,往文件中寫內容,寫完之後,就要關閉該文件,否則會出現什麼情況呢?極端情況下會出現 "Too many open files" 的錯誤,因為系統允許你開啟的最大檔案數量是有限的。

同樣,對於資料庫,如果連接數過多而沒有及時關閉的話,就可能會出現"Can not connect to MySQL server Too many connections",因為資料庫連接是一種非常昂貴的資源,不可能無限制的被創建。

來看看如何正確關閉一個檔案。

普通版:

def m1():
 f = open("output.txt", "w")
 f.write("python之禅")
 f.close()
登入後複製

這樣寫有一個潛在的問題,如果在呼叫write 的過程中,出現了異常進而導致後續程式碼無法繼續執行,close 方法無法被正常調用,因此資源就會一直被該程式佔用者釋放。那麼該如何改進程式碼呢?

進階版:

def m2():
 f = open("output.txt", "w")
 try:
 f.write("python之禅")
 except IOError:
 print("oops error")
 finally:
 f.close()
登入後複製

改良版本的程式是對可能發生例外的程式碼處進行try 捕獲,使用try/finally 語句,該語句表示如果在try 程式碼區塊中程式出現了異常,後續程式碼就不再執行,而直接跳到except 程式碼區塊。而無論如何,finally 區塊的程式碼最終都會被執行。因此,只要把 close 放在 finally 程式碼中,檔案就一定會關閉。

進階版:

def m3():
 with open("output.txt", "w") as f:
 f.write("Python之禅")
登入後複製

一種更簡潔、優雅的方式就是用 with 關鍵字。 open 方法的回傳值賦值給變數 f,當離開 with 程式碼區塊的時候,系統會自動呼叫 f.close() 方法, with 的作用和使用 try/finally 語句是一樣的。那麼它的實作原理是什麼呢?

在講 with 的原理前要涉及到另一個概念,就是上下文管理器(Context Manager)。

上下文管理器

任何實作了__enter__() __exit__() 方法的物件都可稱為上下文管理器,上下文管理器物件可以使用with 關鍵字。顯然,檔案(file)物件也實作了上下文管理器。

那麼文件物件是如何實現這兩個方法的呢?我們可以模擬實作一個自己的檔案類,讓該類別實作 __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__() 方法返回資源對象,這裡就是你將要開啟的那個文件對象, __exit__() 方法處理一些清除工作。

因為 File 類別實作了上下文管理器,現在就可以使用 with 語句了。

with File('out.txt', 'w') as f:
 print("writing")
 f.write('hello, python')
登入後複製

這樣,你就不需要顯示地調用 close 方法了,由系統自動去調用,即使中間遇到異常 close 方法也會被調用。

contextlib

Python 也提供了一個 contextmanager 的裝飾器,更進一步簡化了上下文管理器的實作方式。透過 yield 將函數分割成兩部分,yield 之前的語句在 __enter__ 方法中執行,yield 之後的語句在 __exit__ 方法中執行。緊接在 yield 後面的值是函數的回傳值。

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
 f = open(path, mode)
 yield f
 f.close()
登入後複製

呼叫

with my_open('out.txt', 'w') as f:
 f.write("hello , the simplest context manager")
登入後複製

總結

#

以上是詳細講解python中的關鍵字「with」與上下文管理器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板