這裡介紹 Python 的 with
使用方法,以及自行建立 context manager 的方法與範例程式碼。
資源的管理在程式設計上是一個很常見的問題,例如管理開啟的檔案、網路 socket 與各種鎖定(locks)等,最主要的問題點就在於我們必須確保這些開啟的資源在使用完之後,有確實被關閉(或釋放),如果忘記關閉這些資源,就會造成程式執行上的效能問題,甚至出現錯誤,而除了關閉之外,有些特殊的資源在使用完畢之後,還必須進行一些後續的清理動作,這些也都是資源管理上需要注意的。
with
這個獨特的語法,可讓程式設計者更容易管理這些開啟的資源,在這樣的語法架構之下,Python 程式會自動進行資源的建立、清理與回收動作,讓程式設計者在使用各種資源時更為方便。
with
基本語法傳統上若要開啟一個檔案,我們會這樣寫:
# 開啟檔案 f = open(filename) # ... # 關閉檔案 f.close()
這種寫法會有一個問題,如果在使用檔案的過程中發生了一些例外狀況,造成程式提早跳開時,這個開啟的檔案就會沒有被關閉,所以比較好的程式寫法是使用 try
與 finally
:
# 開啟檔案 f = open(filename) try: # ... finally: # 關閉檔案 f.close()
而上面這種寫法雖然不會有問題,但是缺點就是必須手動加入關閉檔案的程式碼,不是很方便,也很容易忘記。
這種狀況我們就可以改用 with
的寫法:
# 以 with 開啟檔案 with open(filename) as f: # ...
這裡在使用 with
開啟檔案時,會將開啟的檔案一樣放在 f
變數中,但是這個 f
只有在這個 with
的範圍內可以使用,而離開這個範圍時 f
就會自動被關閉,回收相關的資源。
以下是一個實際的範例:
# 以 with 開檔並寫入檔案 with open('file.txt', 'w') as f: f.write('Hello, world!')
使用 with
的話,檔案使用完之後就會自動關閉,方便很多。
若要自行建立 context manager,只要定義好類別的 __enter__
與 __exit__
兩個方法函數即可,with
在剛開始執行時,會執行 __enter__
這個函數,傳回配給的資源(例如開啟的檔案),而在 with
範圍結束時,會自動呼叫 __exit__
釋放資源(例如關閉檔案)。
以下是一個開檔的 context manager 範例:
#/usr/bin/python # -*- coding: utf-8 -*- # 自行定義 Context Manager class File(object): def __init__(self, filename, mode): # 設定檔名與開檔模式 self.filename = filename self.mode = mode # 配給資源(開啟檔案) def __enter__(self): print("開啟檔案:" + self.filename) self.open_file = open(self.filename, self.mode) return self.open_file # 回收資源(關閉檔案) def __exit__(self, type, value, traceback): print("關閉檔案:" + self.filename) self.open_file.close()
使用方式如下:
with File("file.txt", "w") as f: print("寫入檔案...") f.write("Hello, world.")
開啟檔案:file.txt 寫入檔案... 關閉檔案:file.txt
除了使用一般的類別之外,也可以使用 contextlib
這個模組的 decorator 來產生 context manager,以下是一個簡單的開檔範例:
#/usr/bin/python # -*- coding: utf-8 -*- from contextlib import contextmanager # 自行定義 Context Manager @contextmanager def open_file(name, mode): # 配給資源(開啟檔案) f = open(name, mode) yield f # 回收資源(關閉檔案) f.close()
這是使用方式:
with open_file('file.txt', 'w') as f: f.write("Hello, world.")