上下文管理器
語法
with EXPR as VAR:
BLOCK
概念
1. 上下文表達(dá)式:with open('hello.txt') as f:
2. 上下文管理器:open('hello.txt')
3. f 不是上下文管理器,是資源對象
為什么要用上下文管理器?
1.可以以一種更加優(yōu)雅的方式,操作(創(chuàng)建/獲取/釋放)資源,如文件操作、數(shù)據(jù)庫連接;
2.可以以一種更加優(yōu)雅的方式,處理異常;
如何實(shí)現(xiàn)上下文管理器
實(shí)現(xiàn)這樣一個上下文管理,要先知道上下文管理協(xié)議。
簡單點(diǎn)說,就是在一個類里,實(shí)現(xiàn)了enter和exit的方法,這個類的實(shí)例就是一個上下文管理器。
示例
class Resource():
def __enter__(self):
print('===connect to resource===')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('===close resource connection===')
def operate(self):
print('===in operation===')
with Resource() as res:
res.operate()
輸出
===connect to resource===
===in operation===
===close resource connection===
在編寫上下文管理器時,需要將資源的連接或者獲取放在enter中,而將資源的關(guān)閉寫在exit 中
使用contextlib
編寫enter和exit仍然很繁瑣,因此Python的標(biāo)準(zhǔn)庫contextlib提供了更簡單的寫法,上面的代碼可以改寫如下:
from contextlib import contextmanager
class Resource():
def operate(self):
print('===in operation===')
@contextmanager
def create_resource():
print('Begin')
q = Resource()
yield q
print('End')
with create_resource() as f:
f.operate()
很多時候,我們希望在某段代碼執(zhí)行前后自動執(zhí)行特定代碼,也可以用@contextmanager實(shí)現(xiàn)
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
輸出
<h1>
hello
world
</h1>
1.with語句首先執(zhí)行yield之前的語句,因此打印出<h1>;
2.yield調(diào)用會執(zhí)行with語句內(nèi)部的所有語句,因此打印出hello和world;
3.最后執(zhí)行yield之后的語句,打印出</h1>。
@closing
如果一個對象沒有實(shí)現(xiàn)上下文,我們就不能把它用于with語句。這個時候,可以用closing()來把該對象變?yōu)樯舷挛膶ο?。例如,用with語句使用urlopen():
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)
closing也是一個經(jīng)過@contextmanager裝飾的generator,它的作用就是把任意對象變?yōu)樯舷挛膶ο?,并支持with語句,這個generator編寫起來其實(shí)非常簡單:
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
本文采納以下文章部分內(nèi)容: