contextmanager: 上下文管理器類和上下文管理器裝飾器
阿新 • • 發佈:2019-02-17
上下文管理器是在Python2.5之後加入的功能,可以在方便的需要的時候比較精確地分配和釋放資源, with便是上下文管理器的最廣泛的應用, 比如:
with open("test/test.txt","w") as f:
f.write("hello")
這上會比使用try:...finally:f.close
方便的多.
class MyResource:
# __enter__ 返回的物件會被with語句中as後的變數接受
def __enter__(self):
print('connect to resource' )
return self
def __exit__(self, exc_type, exc_value, tb):
print('close resource conection')
def query(self):
print('query data')
類中有兩個特殊的魔術方法:
__enter__: with語句中的程式碼塊執行前, 會執行__enter__, 返回的值將賦值給with句中as後的變數.
__exit__: with語句中的程式碼塊執行結束或出錯, 會執行_exit__
比如以下程式碼:
with Myresource() as r:
r.query()
的列印結果為:
connect to resource
query data
close resource conection
那麼有沒有一個簡化定義的方法呢, python提供了一個裝飾器contextmanager
from contextlib import contextmanager
class MyResource:
def query(self):
print('query data')
@contextmanager
def make_myresource() :
print('start to connect')
yield MyResource()
print('end connect')
pass
被裝飾器裝飾的函式分為三部分:
with語句中的程式碼塊執行前執行函式中yield之前程式碼
yield返回的內容複製給as之後的變數
with程式碼塊執行完畢後執行函式中yield之後的程式碼
比如下方程式碼:
with make_myresource() as r:
r.query()
的結果為:
start to connect
query data
end connect
在程式設計中如果頻繁的修改資料庫, 一味的使用類似try:... except..: rollback() raise e
其實是不太好的.
比如某一段的程式碼的是這樣的:
try:
gift = Gift()
gift.isbn = isbn
...
db.session.add(gift)
db.session.commit()
except Exception as e:
db.session.rollback()
raise e
為了達到使用with語句的目的, 我們可以重寫db所屬的類:
from flask_sqlalchemy import SQLAlchemy as _SQLALchemy
class SQLAlchemy(_SQLALchemy):
@contextmanager
def auto_commit(self):
try:
yield
self.session.commit()
except Exception as e:
db.session.rollback()
raise e
這時候, 在執行資料的修改的時候便可以:
with db.auto_commit():
gift = Gift()
gift.isbn = isbndb.session.add(gift)
db.session.add(gift)
with db.auto_commit():
user = User()
user.set_attrs(form.data)
db.session.add(user)