4.4 flask上下文与with语句

我们上一小节通过手动将app推入栈,弹出栈的方式,解决了working outside application context的问题。实际上更经典的做法是使用with语句来完成。

首先使用with语句替换之前的代码

app = Flask(__name__)

with app.app_context():
    a = current_app
    b = current_app.config["DEBUG"]

什么时候可以使用with语句:

1.实现了上下文协议的对象,可以使用with语句 2.对于实现了上下文协议的对象,我们通常称为上下文管理员 3.通过实现__enter__和__exit__来实现上下文协议 4.上下文表达式必须返回一个上下文管理器

对于上面一段代码来说,AppContext就是上下文管理器;app.app_context()就是上下文表达式。__enter__中做了push操作,__exit__中做了pop操作。 所以只要进入with语句,current_app就是有值的,一旦离开了with语句,current_app 就会弹出,然后就又没有值了(又变成了unbound)。

    def __enter__(self):
        self.push()
        return self

    def __exit__(self, exc_type, exc_value, tb):
        self.pop(exc_value)

        if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
            reraise(exc_type, exc_value, tb)

通过数据库的链接和释放来理解with语句的具体含义

连接数据库的操作步骤: 1.连接数据库 2.sql或者其他的业务逻辑 3.释放资源

如果上面的第二部分出错,那么第三部分的释放资源就不会被执行,资源就会一直被占用。 解决这个问题的通常做法是使用try-except-finally 但是在finally中更优雅的方式就是使用with语句中。我们可以把连接数据库的操作写在上下文管理器的__enter__方法里面,把业务代码写在with语句的代码块里面,把释放资源的语句写在__exit__里面。

读写文件的具体例子

一般的写法

try:
  f = open(r'/Users/test.txt')
  print(f.read())
finally:
  f.close()

使用with语句的写法:

with open(r'/Users/test.txt') as f:
  print(f.read())

注意上面的with语句后面的as 返回的并不是上下文管理器,他实际上是__enter__方法返回的一个值,

exit方法详解

注意我们编写的测试代码,运行时会报错的,错误原因是exit方法接受的参数数量不够。 exit方法的作用不只是释放资源,还有处理异常,所以exit方法还要多接受exc_type,exc_value,tb三个参数。这三个参数在没有异常发生的时候回传控制,如果有异常的话,这三个参数分别是异常类型,异常消息,和详细的异常堆栈信息

exit方法还需要返回一个boolean类型的值,如果返回True,那么外部就不会抛出异常,如果返回False,那么还会在外部抛出异常,如果什么都不返回,按照False处理。Flask提供了一种非常灵活的方式,可以让我们选择在with语句内部还是外部处理异常

Last updated