一来就上代码,问你怕没有
_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
这两段代码显示本文的的重点LocalStack和_RequestContext
先介绍一下_RequestContext,官方文档是这么说的:
"""
The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
"""
简单地说,就是_RequestContext是保留一个request过程中所有的相关信息,如 url adapter和WSGI environment等。app会在处理一个request前将request的相关信息压到_request_ctx_stack栈顶,等到处理完这个request的时候将它弹出。所以回过头来看代码
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
当我们访问flask.request的时候,其实是访问_request_ctx_stack.top这个对象里面的request属性。_request_ctx_stack.top自然就是栈顶了,那么栈顶是什么样一个对象呢?对了,就是刚才介绍的_RequestContext对象了,它在dispatch_request的之前就压入。这个_RequestContext对象里面有request这个属性。所以flask.request其实就是_RequestContext.request。
接下来,我们就要介绍flask的精髓了,Local,LocalStack和LocalProxy这三个类。
class Local(object):
__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)
这个Local类的核心是__ident_func__()函数,这个函数保证了即使在多线程环境下,保存在Local里面的变量不会混乱,而且支持greenlet这个异步协程库。
那么LocalStack是用来干嘛的呢。它的作用是将上面的Local().key = value 变成LocalStack().push(value) 通过list来模拟了堆栈的操作。也是基于Local这个类的。但是为什么需要使用栈呢?
class LocalStack(object):
def __init__(self):
self._local = Local()
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
那么我们继续看看LocalProxy类。代理模式就不多说了,一种设计模式。我们看看代码:
_request_ctx_stack = LocalStack()
_request_ctx_stack.push(_RequestContext())
由上面可以知道我们需要访问的request属性是在_requestContext()里面。那么访问request的时候只能_request_ctx_stack.top.request了。这样子写法没有问题,就是缺少了点优雅。那么如果直接request=_request_ctx_stack.top.request呢。这肯定是不行的,因为在多线程的环境下,request是需要根据当前环境来获得对应的值,这样写相当于写死了request的值了。那么LocalProxy是怎么做的呢?当对LocalProxy属性进行访问的时候,它都会执行一次_get_current_object函数,该函数就是执行我们传入的函数得到访问对象,比如上面的lambda函数就是得到_request_ctx_context.top.request对象。最后再对得到的对象进行属性访问。这样子就可以很优雅地实现了动态获取当前线程的request对象了。高,实在是高!
class LocalProxy(object):
def _get_current_object(self):
if not hasattr(self.__local, '__release_local__'):
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.__name__)
到这里,其实是很清晰这整个过程了。为什么其实在多线程环境下,request对象是根据当前环境取得的值。
参考博文: