Werkzeug源码阅读笔记(三)
这次主要讲下werkzeug中的Local
. 源码在werkzeug/local.py
Thread Local
在Python中,状态是保存在对象中。Thread Local
是一种特殊的对象,它是对线程隔离的。所谓对线程隔离,是指每一个线程对一个Thread Local
对象进行修改,是不会影响到其他线程的。这就好比在工作单位每个人都有一个储物柜,每个人对自己的储物柜存取东西是不会影响到其他人的。这里的储物柜就是Thread Local
.
获得一个Thread Local很简单,只需要对线程执行Local():threading.Local()
在werkzeug
的源码中,Local()
类的实现比较简单,只是定义了几个特殊函数。文件在werkzeug/local.py
中
在该文件中,还定义了release_local(local)
方法,用来释放local对象. 同时,在Local()
类中实现了__release_local__()
函数,它包裹了release_local(local)
函数,调用__release_local__()
可以释放当前Local对象
LocalStack
在werkzeug中同时实现了LocalStack
这个类,它包含了Local()
类的实例,实现了Local
的栈结构,以下是重要部分的代码:
class LocalStack(object):
def __init__(self):
self._local = Local() #LocalStack中有个Local类的对象
def __release_local__(self): #定义释放当前Local对象的方法
self._local.__release_local__()
def _get__ident_func__(self):
return self._local.__ident_func__
def _set__ident_func__(self, value):
object.__setattr__(self._local, '__ident_func__', value)
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
del _get__ident_func__, _set__ident_func__
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):
"""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
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
所有对该对象的修改,只对本线程可见
LocalProxy
在werkzeug中还定义了本地代理。LocalProxy是典型的代理模式的实现。它在构造的时候接受一个callable的参数(实现了__call__()方法的类,一个函数等),这个参数被调用后的返回值本身是一个Thread Local对象。对一个LocalProxy对象的所有操作都会转发到那个callable参数返回的Thread Local对象上。
这是LocalProxy的初始化函数:
def __init__(self, local, name=None):
object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__name__', name)
可以看到,该函数中有个local
参数,这个参数就是callable参数。
下面是Local
类中的__call__()
函数
def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
比如执行:
l = Local()
request = l('request')
这时候就会调用__call__()
函数,request = l('request')
相当于执行:
request = LocalProxy(l, 'request')
这时候request就是一个LocalProxy对象,自动调用初始化函数,该对象有两个元素:_LocalProxy__local
(等于l
)和__name__
(等于'request'
)
所以,对request
的操作会返还给l
进行实际操作
同时注意到LocalStack
类也实现了__call__()
函数,同样可以当做local
参数.
LocalStack
中的__call__
函数:
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError('object unbound')
return rv
return LocalProxy(_lookup)
执行:
_response_local = LocalStack()
response = _response_local()
这时候response
就是一个LocalProxy
,自动调用初始化函数,该对象有两个元素:_LocalProxy__local
(等于_lookup
函数)和__name__
(等于None
)
在LocalProxy
类中,还实现了一个重要的方法:_get_current_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__)
这个方法的作用是获得该LocalProxy
对象背后的真正的对象
LocalManager
在该文件中,还实现了LocalManager
这个类. 因为Local对象不能管理自身,所以可能需要一个LocalManager
对象,可以把多个Local对象传给LocalManager
对象,该对象的每一次清理操作就会清理掉当前上下文中的Local对象
具体的类方法很简单,就不一一赘述了