Python中如何使用class property实现缓存功能

from threading import RLock

_missing = object()

class locked_cached_property(object): “”“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 value. Works like the one in Werkzeug but has a lock for thread safety. “””

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
    self.lock = RLock()

def __get__(self, obj, type=None):
    if obj is None:
        return self
    with self.lock:
        value = obj.__dict__.get(self.__name__, _missing)
        if value is _missing:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value

In [27]: class B:
    ...:     @locked_cached_property
    ...:     def foo(self):
    ...:         import time
    ...:
    ...:         print('processing....')
    ...:         time.sleep(10)
    ...:         return 'b'
    ...:
    ...:

In [28]: b = B()

In [29]: b.foo
processing....
Out[29]: 'b'

In [30]: b.foo
Out[30]: 'b'

In [31]:

Python中如何使用class property实现缓存功能

1 回复

在Python里,用@property配合缓存机制来避免重复计算很常见。核心思路是:在第一次访问属性时进行计算并存储结果,后续访问直接返回缓存值。

这里有个典型实现,用到了Python的__dict__来存储缓存:

class DataProcessor:
    def __init__(self, data):
        self.data = data
        # 可选:初始化缓存字段,但通常不需要
        # self._cached_result = None

    @property
    def expensive_result(self):
        # 检查缓存是否存在。这里用`__dict__`来避免无限递归。
        # 使用`_cached_result`作为缓存键。
        if '_cached_result' not in self.__dict__:
            print("执行昂贵计算...")
            # 模拟耗时计算
            result = sum(self.data) * 2  # 你的实际计算逻辑在这里
            self.__dict__['_cached_result'] = result
        return self.__dict__['_cached_result']

# 使用示例
processor = DataProcessor([1, 2, 3, 4, 5])
print(processor.expensive_result)  # 第一次访问,触发计算
print(processor.expensive_result)  # 第二次访问,直接返回缓存值

关键点:

  1. 缓存键:我们使用_cached_result作为存储在实例__dict__中的键。直接在@property方法里给self._cached_result赋值会导致无限递归,因为self._cached_result会再次触发@property的getter。所以通过self.__dict__['_cached_result']来绕开property机制。
  2. 惰性计算:只有第一次访问属性时才进行计算。
  3. 只读@property默认创建的是只读属性。如果你需要可写并清除缓存,可以再定义一个setter或deleter。

如果需要可写并清除缓存,可以这样扩展:

class DataProcessor:
    def __init__(self, data):
        self.data = data

    @property
    def expensive_result(self):
        if '_cached_result' not in self.__dict__:
            print("执行昂贵计算...")
            result = sum(self.data) * 2
            self.__dict__['_cached_result'] = result
        return self.__dict__['_cached_result']

    @expensive_result.setter
    def expensive_result(self, value):
        # 设置新值并更新缓存
        self.__dict__['_cached_result'] = value

    @expensive_result.deleter
    def expensive_result(self):
        # 删除缓存,下次访问会重新计算
        if '_cached_result' in self.__dict__:
            del self.__dict__['_cached_result']

# 使用示例
processor = DataProcessor([1, 2, 3])
print(processor.expensive_result)  # 计算并缓存
processor.expensive_result = 100   # 通过setter设置新值
print(processor.expensive_result)  # 输出: 100
del processor.expensive_result     # 删除缓存
print(processor.expensive_result)  # 重新计算

一句话总结:用__dict__直接存储缓存值来避免property递归,实现惰性计算和缓存。

回到顶部