Django @cache_property 原理

徐瑞
2023-12-01
class cached_property(object):
    """
    Decorator that converts a method with a single self argument into a
    property cached on the instance.

    Optional ``name`` argument allows you to make cached properties of other
    methods. (e.g.  url = cached_property(get_absolute_url, name='url') )
    """

    def __init__(self, func, name=None):
        self.func = func
        self.__doc__ = getattr(func, '__doc__')
        self.name = name or func.__name__

    def __get__(self, instance, cls=None):
        if instance is None:
            return self

        res = instance.__dict__[self.name] = self.func(instance)
        return res

装饰器类解释:
1. cache_propery为一个装饰器类,装饰某个函数
2. 定义了一个name参数,可以自己传或者直接读取被装饰的函数名
3. __get__方法表名这个装饰器是个描述符装饰器并且为非数据描述符
4. 这个get方法执行了被装饰的函数并将返回的数据保存到了实例的__dict__里面
5. 属性的读取优先级你应该要了解:数据描述符 > __dict__读取 > 非数据描述符

class RawDb(object):

    @cached_property
    def columns(self):
        return 1


rd = RawDb()
print(rd.__dict__)
print(rd.columns)

print(rd.__dict__)
print(rd.columns)

上面的打印输出:

{}
1

{'columns': 1}
1

解释如下:
1. 实例rd第一次运行rd.columns,会调用__get__方法并将返回的数据保存在实例rd的__dict__里面’
2. 访问rd.columns的时候,为什么会直接去调用描述符的 __get__ 方法?因为当访问的属性为描述符时,会将 rd.columns 转化为RawDb.__dict__[‘columns’].__get__(rd, RawDb) 来访问。
2. 实例rd第二次运行rd.columns,为什么不在运行__get__方法?这是因为__dict__的读取优先于非数据描述符,上面第一次运行我们已经把返回数据保存到了__dict__中,所以直接从__dict__中读取了。

 类似资料: