Werkzeug之Local源码解析

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__() 函数来实现。
'''
01-03 12:09