知道 python 缓存的属性是否已被访问而无需实际访问它

tho*_*len 5 python python-descriptors

有许多方法装饰器的示例可以将方法转换为缓存的属性。但有时,我想检查缓存是否处于“活动”状态,这意味着该属性已被访问并且缓存已被填充。

例如,如果我使用rows缓存将 sql 表存储在 a 中rows,我想根据缓存计算表的长度(如果已填充),但如果没有填充,则通过单独的 sql 调用。如何在rows不触发访问的情况下检查是否已被访问?

这是一个很好的装饰器,取自 David Beazley 的“Python Cookbook”),我正在使用它来满足我的缓存属性需求。我已经增强了它以启用我当前的黑客。

class lazyprop:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            setattr(instance, self.func.__name__ + '__cache_active', True)  # my hack
            return value
Run Code Online (Sandbox Code Playgroud)

使用示例:

>>> class Test:
...     def __init__(self, a):
...         self.a = a
...     @lazyprop
...     def len(self):
...         print('generating "len"')
...         return len(self.a)
>>> t = Test([0, 1, 2])
>>> # See what happens if I ask if there is a 'len' attribute:
>>> hasattr(t, 'len')
generating "len"
3
>>> t.len
5
Run Code Online (Sandbox Code Playgroud)

所以hasattr实际上触发了len方法调用,所以我不能使用它。无论如何,我不想使用它,因为我不是要求属性(键/引用)的存在,而是要求它的值的存在(即先前的计算)。

鉴于标有“我的黑客”的行,我现在可以这样做:

def has_active_cache(instance, attr):
    return getattr(instance, attr + '__cache_active', False)
Run Code Online (Sandbox Code Playgroud)
>>> t = Test([0, 1, 2])
>>> print("Accessed:", has_active_cache(t, 'len'))
Accessed: False
>>> t.len
generating "len"
3
>>> print("Accessed:", has_active_cache(t, 'len'))
Accessed: True
Run Code Online (Sandbox Code Playgroud)

但我相信还有比这更优雅的解决方案。也许一个会与它lazyprop本身“结合”的......

小智 10

仅供参考,属性缓存是 Python 3.8 标准库的一部分functools

https://docs.python.org/3/library/functools.html?highlight=s#functools.cached_property

使用此装饰器,您可以__dict__直接访问类的属性以检查该值是否已缓存。

使用文档中的示例...

import statistics
from functools import cached_property


class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)
Run Code Online (Sandbox Code Playgroud)

然后测试一下...

ds = DataSet(range(1, 20))
ds.stdev
5.627314338711377
ds.__dict__
{'_data': range(1, 20), 'stdev': 5.627314338711377}
ds.variance
31.666666666666668
ds.__dict__
{'_data': range(1, 20), 'stdev': 5.627314338711377, 'variance': 31.666666666666668}
Run Code Online (Sandbox Code Playgroud)