werkzeug(flask)中的local,localstack,localproxy探究
1.关于local
python中有threading local处理方式,在多线程环境中将变量按照线程id区分
由于协程在Python web中广泛使用,所以threading local不再满足需要
local中优先使用greenlet协程,其次是线程id,如下所示:
try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident
另外local中定义了一个__release_local__函数,用于清除本协程(线程)下的所有数据:
def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None)
2 localstack
localstack相当于在本协程(线程)中将数据以stack的形式存储
这是通过封装local来实现的
3 localproxy
如其名字所示,这是local的代理。
下面由flask中的context locals来分析
# context locals _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
首先是current_app,我们首先要知道_find_app长啥样:
def _find_app(): top = _app_ctx_stack.top if top is None: raise RuntimeError(_app_ctx_err_msg) return top.app
ok,_app_ctx_stack,这个东西是一个localstack的对象。
localstack的构造函数里只干了一件事:
self._local = Local()
那 _app_ctx_stack.top是什么意思?其实就是取绑定在self._local的stack(一个list)的顶部值:
@property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None
也就是说,我们传入localproxy的是一个callable的函数,当call的时候,返回当前线程中app stack里面顶部元素所绑定的的app
下面我们需要看localproxy的构造函数了:
def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) if callable(local) and not hasattr(local, '__release_local__'): # "local" is a callable that is not an instance of Local or # LocalManager: mark it as a wrapped function. object.__setattr__(self, '__wrapped__', local)
ok,当我们使用self.__local的时候,实际上使用的是传入的callable object: _find_app
接着分析request:
partial(_lookup_req_object, 'request'),意味着当call _lookup_req_object的时候,'request'会作为第一个参数传入。
那call _lookup_req_object 的时候发生了什么?
def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name)
ok,实际上返回当前线程中request stack里面顶部元素所绑定的的request的属性
session,g的分析与request同理,只不过前者是在request stack上,后者是在app stack上
ref: https://www.jianshu.com/p/3f38b777a621