以class为基础的缓存装饰器(自定义的)
class cached_property(property):
"""A decorator that converts a function into a lazy property. The
function wrapped is called the first time to retrieve the result
and then that calculated result is used the next time you access
The class has to have a `__dict__` in order for this property to
work.
"""
# implementation detail: A subclass of python's builtin property
# decorator, we override __get__ to check for a cached value. If one
# choses to invoke __get__ by hand the property will still work as
# expected because the lookup logic is replicated in __get__ for
# manual invocation.
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
def __set__(self, obj, value):
obj.__dict__[self.__name__] = value
def __get__(self, obj, type=None):
if obj is None:
return self
value = obj.__dict__.get(self.__name__, _missing)
if value is _missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
使用该装饰器
from django.utils.functional import cached_property
class Foo:
@cached_property
def foo(self):
print('========执行foo==========')
a=12
return a
f=Foo()
print(f.foo)
print(f.foo)
print(f.foo)
***************************************************************************
执行结果:
========执行foo==========
12
12
12
cached_property
是 property
的subclass, 复写了 __get__
, __set__
方法.
cached_property
是一个描述器(资料描述器),获取属性的时候优先从描述器获取,即(__get__
).
所以执行流程就是:f.foo
-> __get__
-> 从实例字典(f.__dict__
)获取 -> 如果没有则保存到字典并调用实际方法返回
Django 源码
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