(十九)Flask之自定义实现local对象功能

发布时间:2024年01月04日

第一版:基于函数实现local对象功能

紧接上文,最最直白的实现方式:

import time
from threading import get_ident, Thread


storage = {}


def set(k, v):
    ident = get_ident()
    if ident in storage:
        storage[ident][k] = v
    else:
        storage[ident] = {k: v}


def get(k):
    ident = get_ident()
    return storage[ident][k]


def task(arg):
    set('val', arg)
    time.sleep(2)
    v = get('val')
    print(v)


for i in range(10):
    t = Thread(target=task, args=(i, ))
    t.start()

已经可以实现local对象的功能,但是这样的话使用很不方便!

在这里插入图片描述

第二版:基于面向对象思想实现local对象功能

(1)2.1版本:

import time
from threading import get_ident, Thread


class Local(object):
    storage = {}

    def set(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def get(self, k):
        ident = get_ident()
        return Local.storage[ident][k]


if __name__ == '__main__':
    obj = Local()

    def task(arg):
        obj.set('val', arg)
        time.sleep(2)
        v = obj.get('val')
        print(v)


    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()

(2)2.2版本:

  • 实现local对象的通过.value进行赋值操作:

在这里插入图片描述

import time
from threading import get_ident, Thread


class Local(object):
    storage = {}

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return Local.storage[ident][k]


if __name__ == '__main__':
    obj = Local()

    def task(arg):
        obj.val = arg
        time.sleep(2)
        v = obj.val
        print(v)


    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()

(3)2.3版本:

上述代码存在一个很严重的问题:由于storage是定义为Local类的一个类属性,所以不管创建几个Local()对象,用的都是同一个storage来存!

在这里插入图片描述

优化点就是:让代码变得可以放同一个storage/不同storage!

如下将storage变为实例属性即可(想放一个storage里就实例化一个Local对象,想放不同storage里就分别实例化对应Local对象即可):

在这里插入图片描述

但是上面这会报错,定位到实例化Local对象时报的错,如下:

在这里插入图片描述
在这里插入图片描述

问题分析:

  • 实例化后会触发init方法,在里面有个self.storage={},就会触发setattr方法,里面判断走else,所以又触发setattr

问题解决:

  • 因为Local继承了类object,所以可以使用基类object的setattr来触发父类的setattr来设置storage,这样就在设置实例属性storage的时候

    绕过了setattr的自定义实现:
    在这里插入图片描述

分析:

  • 上述2.3版本已经实现了threading.local。但是为啥已经有现成的了,我们不直接用,还要自行实现呢?
  • 答案很简单,因为threading.local的功能不够用!

比如我们现在要实现为每个协程创建一份空间。上述2.3版本代码只需要更改一处就可以了(下述代码段第三行)!

【greenlet.getcurrent就是获取当前协程的唯一标识】

import time
from threading import Thread
from greenlet import getcurrent as get_ident


class Local(object):
    def __init__(self):
        # self.storage = {}
        object.__setattr__(self, 'storage', {})

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]


if __name__ == '__main__':
    obj = Local()

    def task(arg):
        obj.val = arg
        time.sleep(2)
        v = obj.val
        print(v)


    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()

到现在,我们已经非常牛逼了,因为这就是flask里的源码:

from flask import globals

在这里插入图片描述

在这里插入图片描述

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__()
        storage = self.__storage__
        try:
            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)

会发现这个源码跟上述2.3版本代码一样哦!!!

文章来源:https://blog.csdn.net/qq_44907926/article/details/135375054
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。