【问题标题】:Knowing if a python cached property has been accessed without actually accessing it在没有实际访问的情况下知道是否访问了 python 缓存的属性
【发布时间】:2020-01-14 19:31:17
【问题描述】:

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

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

这是一个不错的装饰器,取自 David Beazley 的“Python Cookbook”)我正在使用它来满足我的缓存属性需求。我已对其进行了增强以启用我当前的 hack。

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

使用示例:

>>> 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

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

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

def has_active_cache(instance, attr):
    return getattr(instance, attr + '__cache_active', False)
>>> 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

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

【问题讨论】:

    标签: python python-descriptors


    【解决方案1】:

    仅供参考,属性缓存是 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)
    

    然后进行测试...

    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}
    

    【讨论】:

    • 谢谢@tyler。我想这是迁移到 3.8 的又一个借口!
    猜你喜欢
    • 1970-01-01
    • 2023-01-25
    • 2013-05-10
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多