Python 的 with 語法使用教學:Context Manager 資源管理器

這裡介紹 Python 的 with 使用方法,以及自行建立 context manager 的方法與範例程式碼。

資源的管理在程式設計上是一個很常見的問題,例如管理開啟的檔案、網路 socket 與各種鎖定(locks)等,最主要的問題點就在於我們必須確保這些開啟的資源在使用完之後,有確實被關閉(或釋放),如果忘記關閉這些資源,就會造成程式執行上的效能問題,甚至出現錯誤,而除了關閉之外,有些特殊的資源在使用完畢之後,還必須進行一些後續的清理動作,這些也都是資源管理上需要注意的。


Python 語言提供了 with 這個獨特的語法,可讓程式設計者更容易管理這些開啟的資源,在這樣的語法架構之下,Python 程式會自動進行資源的建立、清理與回收動作,讓程式設計者在使用各種資源時更為方便。

with 基本語法

傳統上若要開啟一個檔案,我們會這樣寫:

# 開啟檔案
f = open(filename)

# ...

# 關閉檔案
f.close()

這種寫法會有一個問題,如果在使用檔案的過程中發生了一些例外狀況,造成程式提早跳開時,這個開啟的檔案就會沒有被關閉,所以比較好的程式寫法是使用 tryfinally

# 開啟檔案
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

若要自行建立 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.")

參考資料:WikibooksJeff KnuppPython Tipsbradmontgomery

程式設計

1 Comment

  1. Alan Tsui

    期代lambda和yield的教學

Leave a Reply