說道上下文管理首先想到的就是這個:
class MyResource: def __enter__(self): print("查詢開始") return self def __exit__(self, exc_type, exc_val, exc_tb): print("查詢結束") def query(self): print("查詢中") with MyResource() as f: f.query()
執行結果
查詢開始
查詢中
查詢結束
但是我們這里要說的不是這個,
from contextlib import contextmanager class MyResource: def query(self): print("查詢中") @contextmanager def make_myresource(): print("查詢開始") yield MyResource() print("查詢結束") # 這里的f是yield后面返回的實例 with make_myresource() as f: f.query()
python給了我們一個contextmanager,contextmanager最大的好處就是可以將不是上下文處理器的類變成一個類似上下文處理的方式來解決問題。
上面的運行結果依然是:
查詢開始
查詢中
查詢結束
簡單的例子,我想把我一本圖書的名字加上書名號輸出:
from contextlib import contextmanager @contextmanager def make_myresource(): print("《", end='') yield print("》") # 這里的f是yield后面返回的實例 with make_myresource(): print("my world", end='')
《my world》
下面看一個例子:
try: # 此時已經支持事務,commit之前都沒有真正提交 gift = Gift() gift.isbn = isbn # current_user是實例化后的user模型 gift.uid = current_user.id current_user.beans += 0.5 db.session.add(gift) db.session.commit() except Exception as e: db.session.rollback() raise e
這個例子是sqlalchemy操作里時常要使用到的事務回滾的代碼,為了防止本次寫入失敗對下一次寫入的影響,我們時常需要捕捉到異常並回滾到初始狀態。這樣的異常捕獲代碼會多次出現在我們的項目里面,所以我們考慮修改他,如何讓代碼簡化,這時候應當使用contextmanager來解決這個問題。
from contextlib import contextmanager from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy from sqlalchemy import Column, SmallInteger class SQLAlchemy(_SQLAlchemy): @contextmanager def auto_commit(self): try: yield self.session.commit() except Exception as e: self.session.rollback() raise e db = SQLAlchemy()
@web.route('/gifts/book/<isbn>') @login_required def save_to_gifts(isbn): if current_user.can_save_to_list(): with db.auto_commit(): # 此時已經支持事務,commit之前都沒有真正提交 gift = Gift() gift.isbn = isbn # current_user是實例化后的user模型 gift.uid = current_user.id current_user.beans += 0.5 db.session.add(gift)
