首先贴出官方文档地址:http://werkzeug.pocoo.org/doc...
几个local?
threading.local
werkzeug.local模块中的:
Local
LocalStack
LocaProxyjava
why not threading.local?python
threading.local,之前接触过java的,对这个再熟悉不过了。线程局部变量,也就是每一个线程的私有变量,具备线程隔离性。服务器
按咱们正常的理解,应该是每个http请求对应一个处理线程。那么这样看来使用threading.local应该够了,为何werkzeug还本身搞了一套?装逼?非也。session
在python中,除了线程以外,还有个叫协程
的东东,(这里不提进程)。java中貌似是没法实现协程的。而python的协程感受高大尚的样子,python3.5开始对协程内置支持,并且也有相关开源库greenlet等。app
协程是什么?
举个例子,好比一个线程在处理IO时,该线程是处于空闲状态的,等待IO返回。可是此时若是不让咱们的线程干等着cpu时间片耗光,有没有其余办法,解决思路就是采用协程处理任务,一个线程中能够运行多个协程,当当前协程去处理IO时,线程能够立刻调度其余协程继续运行,而不是干等着不干活。ide
这么一说,咱们知道了协程会复用线程
,WSGI不保证每一个请求必须由一个线程来处理,若是WSGI服务器不是每一个线程派发一个请求,而是每一个协程派发一个请求,因此若是使用thread local变量可能会形成请求间数据相互干扰,由于一个线程中存在多个请求。
因此werkzeug给出了本身的解决方案:werkzeug.local
模块。函数
from werkzeug.local import Local, LocalManager local = Local() local_manager = LocalManager([local]) def application(environ, start_response): local.request = request = Request(environ) ... application = local_manager.make_middleware(application)
Local配合LocalManager会确保无论是协程仍是线程,只要当前请求处理完成以后清除Local中对应的内容。this
>>> loc = Local() >>> loc.foo = 42 >>> release_local(loc) >>> hasattr(loc, 'foo')
固然,你也能够调用werkzeug.local.release_local(local)手动释放Local或者LocalStack ,可是不能清除代理对象LocalProxy(代理对象底层保留了对Local对象的引用,以便在以后释放)的数据。线程
>>> ls = LocalStack() >>> ls.push(42) >>> ls.top 42 >>> ls.push(23) >>> ls.top 23 >>> ls.pop() 23 >>> ls.top
LocalStack,与Local相似,可是管理数据的方式是采用栈的方式,能够经过LocalManager对象强制释放,可是不建议这么作,而是经过其pop方法弹出。代理
from werkzeug.local import Local l = Local() # these are proxies request = l('request') user = l('user') from werkzeug.local import LocalStack _response_local = LocalStack() # this is a proxy response = _response_local()
werkzeug.local.LocalProxy:Local对象的一个代理。若是你须要建立Local或LocalStack对象的代理,能够直接call。
session = LocalProxy(lambda: get_current_request().session) from werkzeug.local import Local, LocalProxy local = Local() request = LocalProxy(local, 'request') >>> from werkzeug.local import LocalProxy >>> isinstance(request, LocalProxy) True
你也能够经过LocalProxy构造一个代理对象,参数为能够调用的对象或者函数。
_get_current_object()返回被代理的对象。
werkzeug.local模块关键部分代码:
import copy from functools import update_wrapper from werkzeug.wsgi import ClosingIterator from werkzeug._compat import PY2, implements_bool try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident def release_local(local): local.__release_local__() class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) 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__() 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 __release_local__(self): self._local.__release_local__() def __call__(self): def _lookup(): rv = self.top if rv is None: raise RuntimeError('object unbound') return rv return LocalProxy(_lookup) def push(self, obj): 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) return stack[-1] else: return stack.pop() @property def top(self): try: return self._local.stack[-1] except (AttributeError, IndexError): return None class LocalManager(object): def cleanup(self): for local in self.locals: release_local(local) def make_middleware(self, app): def application(environ, start_response): return ClosingIterator(app(environ, start_response), self.cleanup) return application @implements_bool class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) 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__)