知道 python 缓存的 属性 是否在没有实际访问它的情况下被访问
Knowing if a python cached property has been accessed without actually accessing it
有很多方法装饰器的例子可以将方法转换为缓存 属性。但有时,我想检查缓存是否为 "active",这意味着该属性已被访问并且缓存已被填充。
例如,如果我使用 rows
缓存来将 sql table 存储在 rows
中,我想计算 rows
的长度我的 table 基于缓存,如果它已被填充,但如果没有,则通过单独的 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
方法调用,所以我不能使用它。无论如何,我不想使用它,因为我不是在要求属性的存在(key/reference),而是它的值的存在(即事先计算)。
给定 'my hack' 标记的行,我现在可以这样做:
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
本身会 "coupled"...
仅供参考,通过 functools
,属性 缓存是 Python 3.8 标准库的一部分
https://docs.python.org/3/library/functools.html?highlight=s#functools.cached_property
使用此装饰器,您可以直接访问 class 的 __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}
有很多方法装饰器的例子可以将方法转换为缓存 属性。但有时,我想检查缓存是否为 "active",这意味着该属性已被访问并且缓存已被填充。
例如,如果我使用 rows
缓存来将 sql table 存储在 rows
中,我想计算 rows
的长度我的 table 基于缓存,如果它已被填充,但如果没有,则通过单独的 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
方法调用,所以我不能使用它。无论如何,我不想使用它,因为我不是在要求属性的存在(key/reference),而是它的值的存在(即事先计算)。
给定 'my hack' 标记的行,我现在可以这样做:
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
本身会 "coupled"...
仅供参考,通过 functools
https://docs.python.org/3/library/functools.html?highlight=s#functools.cached_property
使用此装饰器,您可以直接访问 class 的 __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}