首页 > 其他 > 详细

# flask local,localstack,localproxy,g

时间:2020-06-16 22:09:33      阅读:76      评论:0      收藏:0      [点我收藏+]

flask local,localstack,localproxy,g


1. 简介

在学习falsk过程中,不可避免会遇到上下文,g,现在了深入了解一下它们的实现原理。
其实它们都是一套班子。

  
# flask.globals.py  
# context locals  
_request_ctx_stack = LocalStack()  
_app_ctx_stack = LocalStack()  
current_app = LocalProxy(_find_app)  
request = LocalProxy(partial(_lookup_req_object, "request"))  
session = LocalProxy(partial(_lookup_req_object, "session"))  
g = LocalProxy(partial(_lookup_app_object, "g"))  

重点概念local,localstack,localproxy。

2. local

  
class Local(object):  
    __slots__ = ("__storage__", "__ident_func__")  

    def __init__(self):  
        object.__setattr__(self, "__storage__", {})  
        object.__setattr__(self, "__ident_func__", get_ident)  

    def __iter__(self):  
        return iter(self.__storage__.items())  

    def __call__(self, proxy):  
        """Create a proxy for a name."""  
        return LocalProxy(self, proxy)  

    def __release_local__(self):  
        self.__storage__.pop(self.__ident_func__(), None)  

    def __getattr__(self, name):  
        try:  
            return self.__storage__[self.__ident_func__()][name]  
        except KeyError:  
            raise AttributeError(name)  

    def __setattr__(self, name, value):  
        ident = self.__ident_func__()   # 获取当前线程/协程的id  
        storage = self.__storage__ # 具体存储信息的dict  
        try: # 处理ident不存在于storage中的情况  
            storage[ident][name] = value  
        except KeyError:  
            storage[ident] = {name: value}  

    def __delattr__(self, name):  
        try:  
            del self.__storage__[self.__ident_func__()][name]  
        except KeyError:  
            raise AttributeError(name)  

它的主要属性如下:
Local().storage = {ident_1:dict1, ident_2:dict2}
ident_1/2为线程号/协程号
取值时也是根据线程号找到对应的dict.name

另外需要说明的是ident获取方法声明如下:
实现了线程与协程号获取。

  
try:  
    from greenlet import getcurrent as get_ident  
except ImportError:  
    try:  
        from thread import get_ident  
    except ImportError:  
        from _thread import get_ident  

3. localstack

它基本是local的队列版。

  
class LocalStack(object):  
    def __init__(self):  
        self._local = Local()  

    def __call__(self):  
        def _lookup():  
            rv = self.top  
            if rv is None:  
                raise RuntimeError("object unbound")  
            return rv  

        return LocalProxy(_lookup)  

    def push(self, obj):  
        """Pushes a new item to the stack"""  
        rv = getattr(self._local, "stack", None)  
        if rv is None:  
            self._local.stack = rv = []  
        rv.append(obj)  
        return rv  

    def pop(self):  
        """Removes the topmost item from the stack, will return the  
        old value or `None` if the stack was already empty.  
        """  
        stack = getattr(self._local, "stack", None)  
        if stack is None:  
            return None  
        elif len(stack) == 1:  
            release_local(self._local)  
            return stack[-1]  
        else:  
            return stack.pop()  

    @property  
    def top(self):  
        """The topmost item on the stack.  If the stack is empty,  
        `None` is returned.  
        """  
        try:  
            return self._local.stack[-1]  
        except (AttributeError, IndexError):  
            return None  

为local()实例添加了_local队列存储dict对象,实现push,pop,top做存取。
应用上下文和请求上下文就是基于它的。

4. localproxy

比较绕的是localproxy

  
@implements_bool  
class LocalProxy(object):  
    __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")  

    def __init__(self, local, name=None):  
        object.__setattr__(self, "_LocalProxy__local", local)  
        object.__setattr__(self, "__name__", name)  
        if callable(local) and not hasattr(local, "__release_local__"):  
            # "local" is a callable that is not an instance of Local or  
            # LocalManager: mark it as a wrapped function.  
            object.__setattr__(self, "__wrapped__", local)  

    def _get_current_object(self):  
        """Return the current object.  This is useful if you want the real  
        object behind the proxy at a time for performance reasons or because  
        you want to pass the object into a different context.  
        """  
        if not hasattr(self.__local, "__release_local__"):  
            return self.__local()  
        try:  
            return getattr(self.__local, self.__name__)  
        except AttributeError:  
            raise RuntimeError("no object bound to %s" % self.__name__)  

    @property  
    def __dict__(self):  
        try:  
            return self._get_current_object().__dict__  
        except RuntimeError:  
            raise AttributeError("__dict__")  

    def __getattr__(self, name):  
        if name == "__members__":  
            return dir(self._get_current_object())  
        return getattr(self._get_current_object(), name)  

    def __setitem__(self, key, value):  
        self._get_current_object()[key] = value  

    def __delitem__(self, key):  
        del self._get_current_object()[key]  


    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)  
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)  

object.setattr(self, "_LocalProxy__local", local)等效于self.__local = local,具体参考私有变量名轧压。这么写是为了避免与下文的魔术方法重载冲突。

LocalProxy通过_get_current_object来获取代理的对象。需要注意的是当初始化参数为callable对象时,则直接调用以返回Local或LocalStack对象,具体看源代码的注释。

核心就一句 if not hasattr(self.__local, "release_local"):
return self.__local()
对于g而言,通过还是执行def _lookup_app_object(name):

localproxy实质是代理类,重载了绝大多数操作符,调用LocalProxy的相应操作时,通过_get_current_object method来获取真正代理的对象,代理了绝大多数操作。

说老实话,不是非常理解为什么非要实现这一代理,使用_app_ctx_stack.top.getattr(name)也是可以实现相同功能的。

4.1. 关于g,session,request

三者是类似的,这里以g为例。
回看flask.globals

  
def _lookup_app_object(name):  
    top = _app_ctx_stack.top  
    if top is None:  
        raise RuntimeError(_app_ctx_err_msg)  
    return getattr(top, name)  

g = LocalProxy(partial(_lookup_app_object, "g"))  

结合上文localproxy所述,g实质是_app_ctx_stack.top.g
而_app_ctx_stack中所推进的对象是
class AppContext(object):

  
class AppContext(object):  
    def __init__(self, app):  
        self.app = app  
        self.url_adapter = app.create_url_adapter(None)  
        self.g = app.app_ctx_globals_class()  

g = class _AppCtxGlobals()
它实质是一个简单的类,用于存储数据。
A plain object. Used as a namespace for storing data during an application context.

g在同一请求环境中可用的特性主要源自top = _app_ctx_stack.top,把self.g = app.app_ctx_globals_class()换成g={}也没有影响。

# flask local,localstack,localproxy,g

原文:https://www.cnblogs.com/wodeboke-y/p/13144414.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!