python的上下文管理
说道上下文管理首先想到的就是这个:
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)