• 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__() 函数来实现。
    '''
    希望你眼眸有星辰,心中有山海,从此以梦为马,不负韶华
  • 相关阅读:
    物质的物理属性·基础整理
    点分治
    洛谷 P3806 【模板】点分治1
    因式分解
    小石潭记
    反演原理及二项式反演
    《庄子》二则
    FFT快速傅里叶变换
    mysql 密码相关
    django基础
  • 原文地址:https://www.cnblogs.com/daviddd/p/11916986.html
Copyright © 2020-2023  润新知