Flask:上下文管理

Posted neozheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask:上下文管理相关的知识,希望对你有一定的参考价值。

1. werkzurg

from werkzur.serving import run_simple

def run(environ,start_response):
    reuturn [bhello world]
    
if __name__ == "__main__":
    run_simple(localhost,4000,run)    # run_simple  --> 启动监听接收 socket(一个死循环); run 会加上 () 去执行

2. 所有请求的入口

def __call__(self, environ, start_response):        # 当请求来的时候,才会执行 __call__ 方法
    """The WSGI server calls the Flask application object as the
    WSGI application. This calls :meth:`wsgi_app` which can be
    wrapped to applying middleware."""
    return self.wsgi_app(environ, start_response)        # wsgi_app()  真正去执行源码
    
# app.run()  这行代码执行的时候,并没有执行 __call__ 方法

3. Local()  ---> 把不同线程的数据隔离

import time
import threading

# 获取 线程或者协程的唯一id
try:
    import greenlet
    get_ident = greenlet.getcurrent
except Exception:
    get_ident = threading.get_ident
    
    
class Local(object):
    DIC = {}    # 数据都放到这个大字典中
    
    def __getattr__(self,item):
        get_ident = get_ident()
        if get_ident in self.DIC:
            return self.DIC[get_ident].get(item)
            
        return None
        
        
    def __setattr__(self,key,value):
        get_ident = get_ident()
        
        if get_ident in self.DIC:
            self.DIC[get_ident][key] = value
            
        else:
            self.DIC[get_ident] = {key:value}
        
    
# __getattr__ 和  __setattr__ 的用处  ; 同一个对象的同一个属性,根据线程和协程号这个唯一标识,来获取不同的值 

4. 上下文管理(第一次)

请求到来时:
    # ctx = RequestContext(self,environ)  # self 是 app对象,environ表示请求相关的原始数据
    # ctx.request = Request(environ)  
    # ctx.session = None
    
    # 将包含了 request和session 的ctx对象打包放到某个地方(相当于一个大字典;根据线程或协程加个唯一标识,放进去的,所以数据相互隔离)
        {
            1232:{ctx:ctx对象},
            ...
        }
视图函数:
    from flask import request,session
    # 上述代码背后的过程:根据当前线程或者协程的唯一标识,取到 ctx对象,然后取到 request和session
请求结束:
    根据当前线程的唯一标识,将 大字典 中该线程对应的数据移除

5. 偏函数

import functools

def func(a,b):
    return a+b
    
new_func = functools.partial(func,6)    # 第一个参数是函数名;第二个参数 6 会当作 func 的第一个参数自动传入 func 中

ret = new_func(2)        # new_func(2) 执行时, 就是 func 函数在执行,6作为第一个参数自动传入 func(),2作为第二个参数传入func
print(ret)

# 偏函数 ---> 帮助开发人员自动传递参数

6. 基于列表维护一个栈:

class Stack(object):
    """
    基于列表维护一个栈
    """
    def __init__(self):
        self.stack = []

    def push(self,item):
        self.stack.append(item)

    def pop(self):
        return self.stack.pop()

    def top(self):
        """
        读取最后一个值
        """
        return self.stack[-1]

7. flask 中的  Local 类 和 LocalStack 类

# 自己写的 Local:
try:
    from greenlet import getcurrent as get_ident
except Exception as e:
    from threading import get_ident

class Local(object):
    """docstring for Local"""
    def __init__(self, arg):
        object.__setattr__(self,"storage",{})        # Local 实例化的时候会在对象中存一个 "storage" 的空字典;注意这种设置对象属性值点语法的方法
        # 这是不能使用 self.storage = {}  ,因为 self.storage 会调用下面的 __setattr__ ,__setattr__ 中也有 self.storage

    def __setattr__(self,key,value):
        get_ident = get_ident()        # 线程号

        if get_ident not in self.storage:
            self.storage[get_ident] = {key,value}
        else:
            self.storage[get_ident][key] = value


    def __getattr__(self,item):
        get_ident = get_ident()

        if get_ident in self.storage:
            return self.storage[get_ident].get(item)

        return None


# 实现的效果: 一个对象, .同一个属性的时候 可获取到不同的值(根据当前线程号等唯一标识)---> 为每个线程开辟一块独立的空间


# flask 中的 Local 和 LocalStack 类:

# flask 源码中的 Local 部分源码:
# 查询“路径”: flask 中的 globals  ---> LocalStack  ---> Local 

try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


class Local(object):
    # 类中的 __slots__() 的作用:只允许该类的实例添加 __slots__ () 中的属性
    __slots__ = (__storage__, __ident_func__)

    def __init__(self):
        object.__setattr__(self, __storage__, {})
        object.__setattr__(self, __ident_func__, get_ident)

    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__()
        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)


obj = Local()
obj.stack = []        
"""
此时 obj. 的时候, 并不是给 obj 对象新添加了一个属性,而是把 "stack" 作为 key,[] 作为 value 放到了 __storage__ 这个大字典中,如下:
{
    "线程号":{"stack":[]}
}
"""
obj.stack.append("abc")
obj.stack.append("123")
obj.stack.pop()
# 通过重写 __setattr__ 方法,Local对象所有通过 . 方法设置属性值时,都把 属性名和其对应的值放到了 __storage__ 这个大字典当前线程号对应的字典中;
# 通过重写 __getattr__ 方法,Local对象通过 . 方法获取属性值时,也是从 __storage__ 这个大字典中 当前线程号对应的字典中 获取该属性key 对应的值

"""
LocalStack 存的数据格式:
__storage__ = {
    1231:{"stack":[]},
    ...
}
"""

# 每次 obj.stack 这个栈 append 或者 pop 时都要加上 .stack ,下面做简化: 用一个代理 LocalStack

class LocalStack(object):
    def __init__(self):
        self._local = Local()        # 实例化 Local ---> 得到一个 __storage__ 属性对应的空字典 :  Local()对象.__storage__ = {}

    def push(self,item):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, stack, None)        # self._local.stack  ---> 触发 Local 的 __getattr__ 方法

        if rv is None:
            self._local.stack = rv = []            # self._local.stack 和 rv 同时指向同一个引用; 此处执行 __setattr__ 方法
        rv.append(item)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self._local, stack, None)
        if stack is None:
            return None
        elif len(stack) == 1:
            return stack[-1]        # 读取栈中的最后一个值
        else:
            return stack.pop()

ls = LocalStack()
ls.push("abc")
ls.push("123")
ls.pop()

"""
__storage__ = {
    "线程号":{"stack":[]}
}
"""

# LocalStack 对象相当于一个代理,帮助我们在Local中维护一个列表,并把这个列表维护成一个栈


"""
小结:
Local 作用: 为每个线程开辟一个独立的空间 (内部维护了一个 __storage__ 的大字典,该字典中的key是当前线程号,字典的value是该线程独立的数据)
LocalStack 作用: 帮助我们把 Local 中的一个列表维护成一个栈
"""

 

以上是关于Flask:上下文管理的主要内容,如果未能解决你的问题,请参考以下文章

Flask 之 上下文管理

flask 上下文管理 &源码剖析

Flask 上下文管理

python使用上下文对代码片段进行计时,非装饰器

flask上下文管理

flask之上下文管理