打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Python:Flask进击篇—Flask的上下文管理

背景

在Flask中可直接导入from flask import request, current_app, g并直接使用,那Flask是如何保证这个request对象就是这个请求对象呢?

Flask官方中有提到使用的是本地线程对象,这篇文章就来揭示其原理

Flask线程间上下文安全

Falsk完成线程安全的原理,是在启动之后进程里维护request栈和app栈,栈是通过线程ID来保证每个请求的线程安全。

实现主要依赖三个类Local,LocalStack和LocalProxy,下面看一下具体的实现原理

三个类构建本地数据

1. Local

先看Local的源码,实质并不是Flask中定义的,而是Flask依赖的werkzeug库所定义。

可以看到其定义的两个属性__storage__, __ident_func__以及三个方法__getattr__,__setattr__,__release_local__。

1.属性__storage__是多层级字典,第一层key是隐含的线程ID或者协程ID,第二层的key是实际使用的关键字

2.属性__ident_func__可以看到是get_ident函数,get_ident函数要么是通过thread库获取当前执行单元的线程ID,要么是通过greenlet库获取当前执行协程的协程ID。

另外可以看到Local这个类的三个方法,实质是通过重写Python内置函数__setattr__和__getattr__来实现线程或者协程间数据隔离

1.获取local某属性时如:local.age实质触发的是__getattr__方法先获取到当前线程ID——__ident_func__函数获取,然后在__storage__字典中找到线程ident对应的结果集从获取到的结果中再查找age属性

2.设置local某属性时如:local.age = 12 实际触发的是__setattr__方法先获取到当前线程ID——__ident_func__函数获取,然后在__storage__字典设置相应的属性字典集

另一个__release_local__方法就是将相应的线程数据删除。

画个简图比较起来更直观一些。

主线程中生成一个对象local=Local(),三个线程中进行相同的操作local.no=每个线程对应的数。为每个线程都开辟一个存储,所以谁来取或者存就找到自己对应中的位置,虽然取得key都一样,但是每次存取都是只关于自己的值。

2. LocalStack

LocalStack也是定义在Flask所依赖的werkzeug库,从字面意思来理解,它就是Local的堆栈操作,看一下源码如何定义。

LocalStack实质就是围绕着Local来进行操作,根据上面我们读完Local的源码可以看到,

  1. LocalStack定义了一个Local对象

  2. 给这个对象设置了一个stack属性,且这个属性是一个列表

  3. LocalStack中定义了对这个列表进行压栈,出栈等方法

  4. 给类中的Local对象提供了自定义ident_func的方法

3. LocalProxy

LocalProxy字面意思就是做一个Local的代理,我们先从一个request的定义来看LocalProxy的用法,然后结合源码来看LocalProxy到底是用来做什么?

类中稍微有些难理解的就是关于object.__setattr__(self, “_LocalProxy__local”, local)的作用,实际就是给self设置一个__local属性。这是Python类中关于私有变量的定义。可以看Python的官方定义python私有变量。

可以看到这个类将所有Python类所内置的方法都进行重写,重写后所有的操作都是基于类中所定义的_get_current_object方法返回的对象进行操作。

而这个方法中返回值就是初始化时所给定的local对象执行返回的结果。如果创建时指定的不是Local对象,则直接执行此方法。如果给定的是Local对象,则根据类名查找对应的对象。

现在这个比较抽象,这个代理到底是做的什么? 我们结合Flask定义全局的request对象来看。假如我们想获取请求的方法是什么,那我们使用的就是request.method。

下面是request定义的源码

  1. 根据LocalProxy的源码中重写的__getattr__方法,先执行_get_current_object方法获取到对象,然后再获取返回对象method属性。

  2. 创建LocalProxy时传递的函数是_lookup_req_object的偏函数,实际就是_lookup_req_object且name=request

  3. 再LocalProxy中__local就是一个函数,所以在执行_get_current_object就是执行_lookup_req_object且name=request返回的值,然后再取其method属性

  4. 此时再执行_lookup_req_object函数,从_request_ctx_stack获取top的request

使用Proxy可以简单快捷的使用request.method获取相应的值,其核心就是每次获取时都会执行对应的函数,而函数中每次返回的值都是线程安全。保证数据正确且优雅。 否则我们每次都去执行一个函数来获取其值,然后再取其属性。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Flask源码解析:上下文
编程语言flask上下文
一个 Flask 应用运行过程剖析
Flask 在蓝图应用程序中访问Flask全局变量|极客教程
超实用的Flask入门基础教程,新手必备!
python:接口开发
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服