flask-上下文管理
预备知识点
threading.local()
import time import threading #当每个线程在执行 val1.xx=1,在内部会为此线程开辟一个空间,来存储 xx=1 #val1.xx,找到此线程自己的内存地址去取自己存储的 xx val1 = threading.local() def task(i): val1.num = i time.sleep(1) print(val1.num) for i in range(4): t = threading.Thread(target=task, args=(i,)) t.start()
自定义threading.local()
import threading """ storage = { 1231:{'x1':0}, 1232:{'x1':1}, ... } """ class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self,key,value): # threading.get_ident() 获取线程id ident = threading.get_ident() if ident in self.storage: self.storage[ident][key] = value else: self.storage[ident] = {key:value} def __getattr__(self,item): ident = threading.get_ident() if ident not in self.storage: return return self.storage[ident].get(item) local = Local() def task(args): local.x1 = args print(local.x1) for i in range(5): t = threading.Thread(target=task, args=(i,)) t.start()
总结:
在 flask 中有个 1ocal 类,他和 threading.local 的功能一样,为每个线程开辟空间进行存取数据。他们两个的内部实现机制,内部维护一个字典,以线程(协程)ID 为 key,进行数据隔离,如 __storage__={ 11211:{'k1':123} } obj = Local() obj.k1 = 123 在 f1ask 中还有一个 Local Stack 的类,他内部会依赖 local 对象,local 对象负责存储数据,localstack 对象用于将local 中的值维护成一个栈 __storage__={ 11211:{'stack':['k1', ]} } obj = LocalStack() obj.push('k1') obj.top obj.pop()
flask 源码中总共有2个localstack对象
# context locals __storage__ = { 1111:{'stack':[RequestContext(reqeust,session),]}, 1123:{'stack'[RequestContext(reqeust,session),]}, } _request_ctx_stack = LocalStack() __storage__ = { 1111:{'stack':[AppContenxt(app,g),]}, 1123:{'stack':[AppContenxt(app,),]}, } _app_ctx_stack = Localstack() _request_ctx_stack('小魔方') _app_ctx_stack('大魔方')
流程整体概要
flask源码分析
flask2.0.x之后版本local实现可以参考下如下文档:
https://www.cnblogs.com/Blogwj123/p/16503429.html
代理LocalProxy解读
https://zhuanlan.zhihu.com/p/269373246
1)项目启动
2)实例化Flask对象
app = Flask(__name__)
1.对app对象封装一些初始化的值。 static_url_path static_folder template_folder 2.添加静态文件的路由 self.add_url_rule( self.static_url_path + "/<path:filename>", endpoint = "static", host = static_host, view_func = self.send_static_file, )
加载配置文件(给 app 的 config进行赋值)
from flask import Flask app = Flask(__name__,static_ur1_path='/xx') app.config.from_object ('xx.xx')
1.读取配置文件中的所有键值对,并将键值对全都放到 Config对象。(Config是一个字典)
2.把包含所有配置文件的 Config对象,赋值给 app.config
3)有用户请求进来
-
创建 ctx=RequestContext 对象,其内部封装了 Request对象和 session 数据。
-
创建 app_ctx=AppContext 对象,其内部封装了 App 和 g。
-
然后 ctx.push 触发将 ctx 和 app_ctx 分别通过自己的 LocalStack 对象将其放入到 Local 中,Local 的本质是以线程 ID 为 key,以{"stack":[]}为 valuel 的字典。
{ 1111:{"stack":[ctx,]} } { 1111:{"stack":[appp_ctx,]} }
-
执行 所有的before_request函数
-
执行视图函数
-
执行所有的after_request函数(session加密放到cookie中)
-
源码流程口述版总结:
1.上下文 -threading.Local 和 Flask 自定义 Local 对象 -请求到来 - ctx = 封装 RequestContext(request,session) - ctx 放到 Local中 -执行视图时 -导入 request - print(request) --> LocalProxy 对象的__str__ - request.method --> LocalProxy 对象的__getattr__ - request + 1 --> LocalProxy 对象的__add__ - 调用 _lookup_req_object函数:去 local 中将 requestContext获取到,再去 requestContext 中获取 request 或 session -请求结束 - ctx.auto_pop() - 将ctx 从 local 中移除
from flask import Flask,g app = Flask(__name__,static_ur1_path='/xx') @app.before_request def f1(): g.×1 = 123 @app.route('/index') def index(): print(g.x1) return 'hello world' if __name__=='__main__': app.run()
测试查看
from flask import Flask, current_app, globals, _app_ctx_stack app = Flask("app01") with app.app_context(): print(_app_ctx_stack._storage.get({})) print(current_app) """ [<flask.ctx.AppContext object at 0x10ed20d00>] <Flask 'app01'> """
问题:
问题 1:多线程是如何体现? 问题 2:1ask 的 1oca1 中保存数据时,使用列表创建出来的栈。为什么用栈? -如果写 web 程序,web 运行环境:栈中永远保存 1 条数据(可以不用栈) -写脚本获取 app 信息时,可能存在 app 上下文嵌套关系 某个值+括号 -函数/方法 -类 -对象 特殊的双下划线方法: __new__ __call__ __str__ __setattr__ __setitem__ __enter__ __exit__ __add__ Ps:F1ask 的 LocalProxy 中全部使用。