使用 contextlib 自動關閉資源
阿新 • • 發佈:2019-01-01
在Python中, 總有一些需要"善後"的事情要做, 比如說開啟檔案後自動關閉檔案描述符, 比如說想要顯示的釋放某種資源, 比如...
上面這種需求很常見的一個場景就是讀取檔案, 很方便的一種做法是使用with
語句來控制
with open('a.txt' 'r') as f:
f.readline()
這樣寫的好處是, 在with
裡邊的程式碼塊執行完畢後, 會自動的關閉關閉檔案. 而且這種寫法可讀性高, 犯錯的機率也會變小.
那麼, 包含with
的程式碼塊在執行的時候都做了什麼呢? 它的執行過程又是怎樣的呢?
- 首先會計算表示式的值, 返回一個上下文管理器物件
- 呼叫上下文管理器物件的
__enter__()
- 如果
with
語句中設定了as
目標物件, 將__enter__()
返回的返回值賦給目標物件 - 執行
with
中的程式碼塊 - 呼叫上下文物件的
__exit__()
方法, 如果with
中程式碼塊中有異常, 那麼會將exception_type, exception_value, traceback
傳給__exit__()
, 如果其返回False
那麼異常會被重新丟擲, 否則異常被掛起, 程式繼續執行
OK, 那麼, 什麼是上下文管理器呢?
上下文管理器是一個物件, 它定義了程式在執行過程中的上下文, 處理程式的執行和退出, 實現了上下文管理協議, 即在物件中定義__enter__()
__exit__()
方法簡單講, 就是實現了
__enter__()
和__exit__()
兩個方法的物件, 我們都可以稱之為上下文管理器
例如, 自己實現一個HTML標籤上下文管理器
class HTMLContextManager(object): def __init__(self, tag): self._tag = tag def __enter__(self): print('<{}>'.format(self._tag)) def __exit__(self, exception_type, exception_value, traceback): print('</{}>'.format(self._tag)) return False with HTMLContextManager('h1'): print('I am body')
輸出結果如下
<h1>
I am body
</h1>
可以說是非常的方便了, 在實際的應用當中, 可以把上下文管理器用作鎖的控制, 檔案的開啟關閉, 異常處理等等, 在__enter__()
方法中實現資源分配, 預處理工作, 在__exit__()
方法中實現資源釋放, 善後工作
但是, 如果想把一個函式物件包裝成一個上下文管理器怎麼辦呢? Python的標準庫提供了更加易用的contextlib
上下文管理工具模組, 它可以通過生成器實現, 不需要顯示建立類以及__enter__()
和__exit__()
兩個方法
示例:
@contextmanager
def html_context_manager(tag):
print('<{}>'.format(tag))
yield
print('</{}>'.format(tag))
with html_context_manager('h2'):
print('I am body')
輸出結果如下
<h2>
I am body
</h2>
比寫一個類更加簡便了! 但是這種寫法, 如果with
程式碼塊中的程式碼丟擲了異常, 預設是向上丟擲異常, 程式掛起的, 也就是類中__exit__()
方法返回False
還有一種比較好用的用法是把一個類包裝成一個上下文管理器, 即
class MyClass(object):
def __init__(self):
pass
@contextmanager
def my_context_manager():
print('before call')
yield MyClass()
print('after call')