Werkzeug之Local源码解析
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__() 函数来实现。
'''

浙公网安备 33010602011771号