Werkzeug之Local源码解析
原博客地址
http://liuyajing.coding.me/blogs/python/2018/werkzeug-local/
一、引入
最近在阅读 Flask 的源码,遇到三个概念:Local 、 LocalStack 和 LocalProxy ,本文主要就针对 Local 概念及其源码进行原理剖析。
二、Local
Local 是一个类,源码位置:site-packages/werkzeug/local.py
在模块的开头,有以下代码:
# 由于每个线程都有自己的greenlet,我们可以将它们用作上下文的标识符。
# 如果greenlet不可用,我们将根据它的位置回退到当前的线程标识。
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
1. 定义
class Local(object):
# 定义此类允许绑定的属性名称 tuple
__slots__ = ('__storage__', '__ident_func__')
2. init
def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)
'''
调用object 的__setattr__ 方法设置类Local的两个属性值, 即:
self.__storage__ = {}, self.__ident__func__ = get_ident
'''
3. iter
def __iter__(self):
return iter(self.__storage__.items())
'''
__iter__方法返回一个迭代器,说明Local实例是一个可迭代对象
'''
4. call
def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
'''
在类中实现了 __call__ 方法,那么实例对象也将成为一个可调用对象,那就可以像函数一样调用它。
'''
5. getattr
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
'''
如果 name 被访问,同时它不存在的时候,此方法被调用。
此方法实现了通过直接访问类属性的方式间接获取字典 self.__storage__[self.__ident_func__()]中key为name的值,而name并不属于类的属性,而此函数一定会被调用,很巧妙的 写法。
'''
6. setattr
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
'''
如果要给name赋值,就调用这个方法。
此方法实现了通过直接给类属性赋值达到间接往字典self.__storage__[self.__ident_func__()]中插入{name: value}的目的。
'''
7. delattr
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
'''
如果要删除name,这个方法就被调用。
此方法实现了通过直接删除类属性达到间接从字典 self.__storage__[self.__ident_func__()]中删除key的目的。
'''
8. release_local
def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None)
'''
此方法是为了删除字典self.__storage__中key为self.__ident_func__()及对应的value值。
'''
9. 总结
'''
- Local 类实际是对 dict __storage__ 的封装,而这个dict中 的 key 使用的就是get_indent 函数获取的 id (当有 greenlet时使用 greenlet id,没有则使用 thread id)
- dict __storage__ 中的 value 也是一个 dict,这个 dict 就是该 greenlet (或者thread) 对应的 local 存储空间
- 通过重新实现 __getattr__、__setattr__、__delattr__ 等方法,我们在 greenlet 或者 thread 中使用 Local 实例对象时,可以通过访问类属性的方式 (会自动获取greenlet
- id (或者 thread id)) ,访问到对应的 dict 存储空间中真正存储的对象。
- 这个技巧在实际编写线程安全或协程安全的代码时是非常有用的,即通过 greenlet id ( thread id ) 来分别存储数据。
- 当需要释放 greenlet ( 或 thread ) 对应的存储空间时,可以通过调用__release_local__() 函数来实现。
'''