flask中Localstack和Local对象实现(flask请求封装的实现)

1.源码分析:Local()对象和LocalStack()对象()

复制代码
from flask import globals
点击去globals,可以看到from werkzeug.local import LocalStack, LocalProxy
是通过werkzeug的local实现的flask的local。

源码:
try: from greenlet import getcurrent as get_ident # 注意这是导入的协程的get_ident,说明flask的控制力度更细,也是更牛逼的地方 # 我们可以为线程开辟空间,flask可以为协程开辟空间 except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident class Local(object): def __init__(self): object.__setattr__(self, "__storage__", {}) # self.__storage__ = {} object.__setattr__(self, "__ident_func__", get_ident) # self.__ident_func__ = get_ident def __iter__(self): return iter(self.__storage__.items()) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() # 1111 storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)
复制代码
关于flask里面的local()对象,和在上一篇自定义版普通的threading.local方法的是逻辑一样的.
在Local()对象里面__storage__的格式如下:
__storage__= {
    ident(表示线程的唯一id):{name:value}
}
但是这里牛逼的是LocalStack类。在globals里面有着两个实例化
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
先记住这两个实例化的名字。来看LocalStack类里面有什么牛逼的地方
复制代码
            
class LocalStack(object):
    def __init__(self):
        self._local = Local()
    def push(self, obj):
        """Pushes a new item to the stack"""
        # self._local.stack == getattr
        # rv = None
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        stack = getattr(self._local, "stack", None)
        if stack is None:
            return None
        elif len(stack) == 1:    # 在只有一个值的时候,就销毁,先返回最后一个值,在销毁,存成[],在release_local()里面实现
            # release_local(self._local)
            # del __storage__[1111]
            return stack[-1]
        else:
            return stack.pop()
    @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

obj = LocalStack()
obj.push('kobe')

print(obj.top)
obj.pop()


第一步:
    obj = LocalStack()实力化的时候,会去执行__init__方法
    LocalStack的__init__中 实例化了Local(),并赋值给self._local

第二步:
    在执行obj.push('kobe')的时候,执行了rv = getattr(self._local, "stack", None)
    其中的getattr执行的是self._local的getattr方法,也就是执行self._local.stack。
    注意:这一步是通过self._local的getattr方法在获取值。
    因为此时Local里面的getattr方法是没有值的,所以返回一个None,所以rv=None
第三步:
    继续向下执行self._local.stack = rv = [],此时self._local.stack和rv都指向这个[]的内存地址
    这句话可以拆分成self._local.stack = rv,此时是去执行self._local.setattr方法
    self._local.setattr的name= stack,value等于[]
    所以在执行到这步骤的时候,self._local里面的storage是如下格式
    storage = {
        ident(理解成线程唯一id):{"stack":[]}
    }
第四步:
    执行rv.append(obj),相当于在storage里面添加了kobe
    storage = {
        ident(理解成线程唯一id):{"stack":["kobe",]}
    }
所以说白了,执行push方法的时候,会自动的在self._local.storage里面添加数据

这里面只有push逻辑比较麻烦,top和pop的原理比较简单.
复制代码

 Local()和LocalStack总结:

复制代码
在flask中有个local类,他和上一篇的自定义的threading.local的功能一样,为每个线程开辟空间进行存取数据,他们两个的内部实现机制相同,内部维护一个字典,以线程(协程)ID为key,进行数据隔离,如:
    __storage__ = {
        1211:{'k1':123}
    }
    obj = Local()
    obj.k1 = 123
    
在flask中还有一个LocalStack的类,他内部会依赖local对象,local对象负责存储数据,localstack对象用于将local中的值维护成一个栈。
    __storage__ = {
        1211:{'stack':['k1',]}
    }
    obj= LocalStack()
    obj.push('k1')
    obj.top
    obj.pop()

在flask里面真正使用的都是LocalStack()对象,并没有Local()对象
复制代码

2.flask中Local()和LocalStack()对象的具体作用

复制代码
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
还记得上面的这两个类嘛??

在flask源码内部里面使用了单例模式,创建了两个LocalStack()对象分别赋值,并且只实力化了这两个LocalStack()对象

_request_ctx_stack
  其中_request_ctx_stack的__storage__里面维护的数据如下:
  __storage__ = {
      (线程id)1111:{"stack":[RequestContext(request,session),]}
  }
  RequestContext指的是一个对象,里面存放请求信息request和session信息

_app_ctx_stack
  其中_app_ctx_stack的__storage__里面维护的数据如下:
  __storage__ = {
      (线程id)1111:{"stack":[AppContext对象(app,g),]}
  }
  AppContext指的也是一个对象,里面存放app对象和g信息,app指的就是实例化的时候的那个app信息(里面包含了好多信息,路由,配置文件等)
    
总结:
    flask的请求进来之后,会将所有的信息存成两个对象
        RequestContext对象:存放request请求和session信息,最后存在_request_ctx_stack中
        AppContext对象:存放app对象和g(一般是空字典)信息,最后存在_app_ctx_stack中
    
    所以flask有两个上下文管理:请求上下文管理(RequestContext)和应用上下文管理(AppContext),合在一起叫做flask的上下文管理
复制代码

3.总结

复制代码
那么上面的流程到底在flask中做了什么事情?
  上面的流程就是flask中请求过来之后封装数据的过程。

那么Local和Localstack()对象在请求封装是做了什么?
  Local()对象:和threading.local的功能一样,为每一个即将到来的请求,也就是线程,开辟自己的空间,用于保存该线程的一些数据,进行数据隔离
  LocalStack(): 基于Local对象,将Local对象中每一个线程的数据改成栈的形式来存储,用于添加返回和删除信息。

flask和Django的有一个最重要的区别就是:
  Django的请求是一层一层的封装的,都放在django的缓存里面
  Flask的请求是基于上下文管理来做。
复制代码

 

 flask中请求过来之后的封装流程图:

复制代码

 1.当请求进来之后,将请求分成两个对象
  一个是RequestContext对象:request对象和session
  一个是AppContext对象:app对象和g(空字典)

 2.将对象存放到上下文管理里面,具体步骤如下

  2.1 实例化LocalStack的两个类
    _request_ctx_stack = LocalStack() # 用于存放RequestContext对象的信息
    _app_ctx_stack = LocalStack()     # 用于存放AppContext对象的信息

  2.2 flask实现了两个Local()对象和LocalStack()对象,其中:
     Local()对象:实现了魏每一个线程开辟空间,存储数据,保证数据隔离
     LocalStack()对象:基于Local对象,将保存现成数据的格式变成栈的格式,方便以后增加,取值,和删除信息

  2.3 其中存储的数据格式如下:
      _request_ctx_stack的__storage__里面维护的数据如下:
        __storage__ = {
          (线程id)1111:{"stack":[RequestContext(request,session),]}
        }

    _app_ctx_stack的__storage__里面维护的数据如下:
      __storage__ = {
          (线程id)1111:{"stack":[AppContext对象(app,g),]}
      }  
 2.4 封装好之后,后端就可以调用了
复制代码

 

 

pass

 

posted @   thep0st  阅读(187)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示