LocalStack 和 Local 实现上下文管理

面向对象

class Foo(object):

    def __setattr__(self, key, value):
        print(key,value)
    
    def __getattr__(self, item):
        print(item)

obj = Foo()
obj.x = 123		# 执行setattr
obj.x			# 执行getattr

列表实现栈

后进先出,通过列表可以实现一个栈。

v = [11,22,33]
v.append(44)
v.pop()

问题:
flask为什么需要栈结构?
需要栈结构,能够将两个上下文推到栈中去。

LocalStack 和 Local 解析源码 local.py

try:
    # 获取线程(协程)ID
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident
"""
产生一个字典
__storage__ = {
    1111:{"stack":[汪洋] }
}
"""
class Local(object):

    def __init__(self):
        # self.__storage__ = {}
        # self.__ident_func__ = get_ident
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(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)

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(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('汪洋')
obj.push('成说')
print(obj.top)

obj.pop()

解释:

   local类,和threading.local的功能一样,为每个线程开辟空间进行存取数据,Local内部维护一个字典,以线程(协程)ID为key,进行线程之间数据隔离,如:
    __storage__ = {
		1211:{'k1':123},
                1222:{'k2':123}
    }
    
    obj = Local()
    obj.k1 = 123
    
    LocalStack的类,它内部会依赖local对象,local对象负责存储数据,localstack对象用于将local中的值维护成一个栈(用push、top、pop方法)。
	__storage__ = {
		1211:{'stack':['k1',]}
    }

	obj= LocalStack()
    obj.push('k1')
    obj.top
    obj.pop() 

LocalStack类两个实例对象

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()

也就是说这两个对象都能够各自维护各自的字典(能使用push、top、pop方法属性),其中_request_ctx_stack对象会将请求对象RequestContext(reqeust,session)推入到自己的栈中;
_app_ctx_stack对象会将flask核心对象AppContenxt(app,g),推入到自己的栈中。
RequestContext(reqeust,session) :请求上下文
AppContenxt(app,g):应用上下文

# 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,g),]},
}
_app_ctx_stack = LocalStack()

流程图:

总结:

1. 当请求到来时,首先会创建出两个上下文对象,RequestContext(reqeust,session)封装了request和session、AppContenxt(app,g)封装了app和g;
   LocalStack()实例化两个对象`_request_ctx_stack `、`_app_ctx_stack`;并且在`__init__`初始化时,通过` self._local = Local()` 创建一个_local对象,而Local类会创建一个__storage__大字典,并且会以线程ID作为Key。
    ```
    # 字典结构:
    __storage__ = {
        1111:{},}
    ```
   LocalStack()类的两个实例化对象会执行 `push`方法,`_request_ctx_stack`将` RequestContext(reqeust,session)`对象推入自己的栈中,而`_app_ctx_stack`将`AppContenxt(app,g)`对象推入自己的栈中;
    ```
    # 字典结构:
    __storage__ = {
    1111:{'stack':[RequestContext(reqeust,session),]},}

    __storage__ = {
    1111:{'stack':[AppContenxt(app,g),]},}
    ```

2. 当执行视图函数时,request.form/args、 session等,这时候会执行 `top`方法,去获取RequestContext对象;

3. 当请求结束时,执行`pop`方法将AppContext和RequestContext弹出去。
posted @ 2019-11-23 16:52  SensorError  阅读(196)  评论(0编辑  收藏  举报