Python 上下文管理器和 with 语句
1. 上下文管理器概念
什么是 Python 的上下文管理器(Context Managers)呢?
?
含有 __enter__ 和 __exit__ 方法的對象就是。上下文管理器存在的目的是管理 with 語句,就像是迭代器的存在是為了管理 for 語句一樣。
with 上下文管理器:語句體
2. with 語句
當 with 遇到上下文管理器,就會在執行語句體之前,先執行上下文管理器的 __enter__ 方法,然后再執行語句體,執行完語句體后,最后執行 __exit__ 方法。舉個例子:
class Foo(object):def __init__(self):print('把大象放冰箱總共分幾步?')def __enter__(self):print('把冰箱門打開')def __exit__(self, exc_type, exc_val, exc_tb):print('把冰箱門關上')obj = Foo()with obj:print('把大象放進去')
輸出:
把大象放冰箱總共分幾步?
把冰箱門打開
把大象放進去
把冰箱門關上
with 有一個很重要的作用是簡化 try/finally 模式,無論何種異常發生,finally 字句都會執行。
?
通常 finally子句中的代碼用于釋放重要的資源,或者還原臨時變更的狀態。比如,不管大象能不能裝進冰箱,最終 finally你都得把冰箱門關上,用代碼解釋就是:
class Foo(object):def __init__(self):print('把大象放冰箱總共分幾步?')def __enter__(self):print('把冰箱門打開')def __exit__(self, exc_type, exc_val, exc_tb):print('把冰箱門關上')obj = Foo()with obj:raise ImportError # 這里發生嚴重的錯誤,這段程序崩潰了print('把大象放進去')
輸出結果:
把大象放冰箱總共分幾步?
把冰箱門打開
把冰箱門關上
Traceback (most recent call last):File "char_1.py", line 15, in <module>raise ImportError
ImportError
有了 with 保駕護航,裝大象的程序崩潰了,也能把冰箱門關上。
3. 上下文管理器寫法
Python 提供了更簡便的上下文管理器寫法,使用 @contextmanager。在使用 @contextmanager 裝飾的生成器中,yield 語句的作用是把所在函數分成兩部分:
yield語句前面的代碼在with塊開始時執行;yield語句后面的代碼在with塊結束時執行;
那么上面的例子就可以簡寫成為:
from contextlib import contextmanager@contextmanager
def fridge_operation():print('把大象放冰箱總共分幾步?')print('把冰箱門打開')yieldprint('把冰箱門關上')def put_elephant_in_fridge():print('把大象放進去')with fridge_operation() :put_elephant_in_fridge()
輸出:
把大象放冰箱總共分幾步?
把冰箱門打開
把大象放進去
把冰箱門關上
但需要特別注意的是,如果使用 @contextmanager 來實現上下文管理器,要把 yield 語句放在 try/finally語句中(或者放在 with 語句中),個人覺得 Python 在這一點上把 @contextmanager 變的十分具有迷惑性,它可以把生成器函數變成上下文管理器,但是它不能聯手與 with 默認完成 finally 功能,用代碼說明:
from contextlib import contextmanager@contextmanager
def fridge_operation():print('把大象放冰箱總共分幾步?')print('把冰箱門打開')yieldprint('把冰箱門關上')def put_elephant_in_fridge():print('把大象放進去')with fridge_operation() :raise ImportError # 這里發生嚴重的錯誤,這段程序崩潰了put_elephant_in_fridge()
輸出:
把大象放冰箱總共分幾步?
把冰箱門打開
Traceback (most recent call last):File "char_1.py", line 15, in <module>raise ImportError
ImportError
發現了么?冰箱門沒關上。冰箱門沒關上,我要你上下文管理器有什么用?所以我們需要把 yield 語句放在 try/finally 語句中,如下:
from contextlib import contextmanager@contextmanager
def fridge_operation():print('把大象放冰箱總共分幾步?')print('把冰箱門打開')try:yieldfinally:print('把冰箱門關上')with fridge_operation():raise ImportErrorprint('把大象放進去')
輸出:
把大象放冰箱總共分幾步?
把冰箱門打開
把冰箱門關上
Traceback (most recent call last):File "char_1.py", line 13, in <module>raise ImportError
ImportError
至此,@contextmanager 巧妙的將三個不同的 Python 特性結合到了一起:函數裝飾器,生成器和 with 語句。
?
參考:
https://book.pythontips.com/en/latest/context_managers.html
總結
以上是默认站点為你收集整理的Python 上下文管理器和 with 语句的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国顺丁橡胶行业发展
- 下一篇: 2022-2028年中国氟膜行业市场全景