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,]}
}

注意:以后再想要获取 request/session/app/g 时,都需要去 local 中获取。

  • 执行 所有的before_request函数

  • 执行视图函数

  • 执行所有的after_request函数(session加密放到cookie中)

  • 销毁ctx和app_ctx

源码流程口述版总结:

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 中移除

 g是什么?

在一次请求的周期中,可以在 g 中设置值,在本次请求周期中都可以读取或赋值。

相当于是一次请求周期的全局变量。

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 中全部使用。

 

posted @ 2022-09-08 16:41  耗油炒白菜  阅读(59)  评论(1编辑  收藏  举报