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__() 函数来实现。 '''