Python yield使用詳解(二)
阿新 • • 發佈:2018-11-17
上下文管理器和with塊
with表示式
常見的with用法格式:
with open(filename) as f:
statement
statement
...
with lock:
statement
statement
...
- 控制程式碼塊的
進入/退出
定製你自己的上下文管理器
一個計時器的例子:
import time from contextlib import contextmanager @contextmanager def timethis(label): start = time.time() try: yield finally: end = time.time() print('%s: %0.3f' % (label, end-start)) #Usage with timethis('counting'): n = 1000000 while n > 0: n -= 1 #Output counting: 0.156
另外一個例子:臨時資料夾
import tempfile, shutil
from contextlib import contextmanager
@contextmanager
def tempdir():
outdir = tempfile.mkdtemp()
try:
yield outdir
finally:
shutil.rmtree(outdir)
#Example
with tempdir() as dirname:
...
等等!!!
這裡的yield outdir
是什麼?
- 不是迭代
- 不是資料流
- 不是併發
- 那是什麼???
深入上下文管理器
現在可以對上面內容進行小結一下:
- 上下文物件存在的目的是管理
with
語句,就像迭代器的存在是為了管理for
語句 - with語句的目的是簡化t
try/finally
模式,這種模式用於保證一段程式碼執行完畢後執行某項操作。即使那段程式碼由於異常,return
語句或sys.exit()
呼叫而終止,也會執行指定操作
上下文管理器的內部實現:
- 上下文管理器協議包含enter和exit兩個方法,
with
語句還是執行時,會在上下文管理器物件上呼叫enter方法。with語句結束後,會在上下文管理器物件上呼叫exit
實現模板
class Manager(object):
def __enter__(self):
return value
def __exit__(self, exc_type, val, tb):
if exc_type is None:
return
else:
# Handle an exception (if you want)
return True if handled else False
# Usage
with Manager() as value:
statements
statements
例項
import tempfile
import shutil
class tempdir(object):
def __enter__(self):
self.dirname = tempfile.mkdtemp() #生成臨時資料夾
return self.dirname
def __exit__(self, exc, val, tb):
shutil.rmtree(self.dirname) #刪除資料夾
# Usage
with tempdir() as dirname:
...
# with語句執行完畢後,會自動刪除那個臨時資料夾
更簡潔的一種選擇:利用@contextmanager裝飾器
import tempfile, shutil
from contextlib import contextmanager
@contextmanager
def tempdir():
dirname = tempfile.mkdtemp()
try:
yield dirname
finally:
shutil.rmtree(dirname)
# 跟上個例子相同的程式碼。
contextmanager裝飾器執行原理
下圖:
- 思考剪刀處
yield
程式碼 - 將程式碼一分兩半
- 每一半對應著上下文管理器協議
yield
是促成圖中這一實現的魔法
這裡有一個注意點:使用@contextmanager
裝飾器時,要把yield語句放在try/finally
語句中,這是無法避免的,因為我們永遠不知道上下文管理器的使用者會在with中做什麼(會引發一些python直譯器能捕獲到的錯誤)。
當然,想要你如果想要更加深入的瞭解@contextmanager
的內部程式碼實現,可以檢視原始碼,這裡不展開了。
總結
yield
表示式的另一個不同作用:上下文管理器- 常用來重新定製控制流
- 也可以用
@contextmanager
裝飾器來代替enter和exit兩個方法。優雅且實用,把三個不同的Python特性結合到一起: 函式裝飾器,生成器和with
語句
參考資料
David beazley協程
Fluent Python
作者:尋找無雙丶
連結:https://www.jianshu.com/p/bf887cae4d8e
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。