lru_cache 为所有 class 个实例保存
lru_cache that saves for all class instances
我有什么办法可以 lru_cache
在 python 中 class 级别的 @property
这样即使在返回计算的 属性 时也是如此另一个 class 实例的值,属性 将不会重新计算,而是从缓存中提取。我想实现如下目标:
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@property
@lru_cache
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
t1 = SomeClass(2,3)
t1.sum
>> 5 #calculating
t2 = SomeClass(2,3)
t2.sum
>> 5 #returning cache from t1, NOT calculating
我认为这可以通过使用一个变量来保存已经执行的计算来完成。
我稍微修改了您的代码,让我们看一下。
假设您有以下代码:
CACHED_VALUES = {}
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
self.pair = '{},{}'.format(num1,num2)
if self.pair not in CACHED_VALUES.keys():
print('Calculating from init...')
CACHED_VALUES[self.pair] = self.sum
else:
print('Already stored... Getting the values from cache...')
@property
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
t1 = SomeClass(2,3)
print(t1.sum)
t2 = SomeClass(2,3)
print(t2.sum)
print('This is new instance.')
t3 = SomeClass(2,3)
print(t3.sum)
首先,我创建了 CACHED_VALUES
空字典(暂时)。请注意,它是在 class 之外声明的。
其次,我创建了 self.pair
变量,该变量将两个数字表示为字符串,以逗号分隔。这样做的原因是因为您 不能将 列表作为 字典键 。这就是我们将两个数字连接成字符串的原因。
如果我们应用这种方法,CACHED_VALUES
字典将更新如下:
CACHED_VALUES = {}
t1 = SomeClass(2,3)
print(CACHED_VALUES)
>> {'2,3': 5}
现在介绍 __init__
方法。
我添加了 if 条件来检查 CACHED_VALUES
字典是否已经包含计算值。如果不是 - 执行函数并将返回值保存到字典中。如果它存在 - 那么我们得到已经计算的值,省略函数执行。
您可以在下面看到修改后的代码及其输出:
CACHED_VALUES = {}
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
self.pair = '{},{}'.format(num1,num2)
if self.pair not in CACHED_VALUES.keys():
print('Calculating from init...')
CACHED_VALUES[self.pair] = self.sum
else:
print('Already stored... Getting the values from cache...')
@property
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
print('[X]Creating first instance')
t1 = SomeClass(2,3)
print(t1.sum)
print('[X]Creating second instance')
t2 = SomeClass(2,3)
print(t2.sum)
print('[X]This is instance with different values.')
t3 = SomeClass(5,7)
print(t3.sum)
print('[X]This is second instance with different values.')
t4 = SomeClass(5,7)
print(t4.sum)
# OUTPUT:
[X]Creating first instance
Calculating from init...
5
[X]Creating second instance
Already stored... Getting the values from cache...
5
[X]This is instance with different values.
Calculating from init...
12
[X]This is second instance with same values.
Already stored... Getting the values from cache...
12
所以我找到了一个解决方案,虽然不是使用 funtools.lru_cache
,而是 ring
。根据我的阅读,lru_cache
无法做到这一点,但如果有人知道使用 lru_cache
的解决方案,请随时 post 它,我会将其标记为正确答案.
import ring
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@ring.dict({})
@property
def sum(self):
return self.num1 + self.num2
我正在添加第二个答案,因为我没有足够的声誉来向您的答案添加评论。
您可以查看以下代码片段(摘自https://docs.python.org/3/library/functools.html):
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
或
@lru_cache(maxsize=32)
def get_pep(num):
'Retrieve text of a Python Enhancement Proposal'
resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
try:
with urllib.request.urlopen(resource) as s:
return s.read()
except urllib.error.HTTPError:
return 'Not Found'
>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
... pep = get_pep(n)
... print(n, len(pep))
>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
使用methodtools.lru_cache
的解决方案
from methodtools import lru_cache
called = 0
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@lru_cache()
@classmethod
def _sum(cls, num1, num2):
global called
called += 1
return num1 + num2
@property
def sum(self):
return self._sum(self.num1, self.num2)
if __name__ == '__main__':
assert called == 0
t1 = SomeClass(2, 3)
print(t1.sum)
assert called == 1
t2 = SomeClass(2, 3)
print(t2.sum)
assert called == 1
我有什么办法可以 lru_cache
在 python 中 class 级别的 @property
这样即使在返回计算的 属性 时也是如此另一个 class 实例的值,属性 将不会重新计算,而是从缓存中提取。我想实现如下目标:
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@property
@lru_cache
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
t1 = SomeClass(2,3)
t1.sum
>> 5 #calculating
t2 = SomeClass(2,3)
t2.sum
>> 5 #returning cache from t1, NOT calculating
我认为这可以通过使用一个变量来保存已经执行的计算来完成。 我稍微修改了您的代码,让我们看一下。 假设您有以下代码:
CACHED_VALUES = {}
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
self.pair = '{},{}'.format(num1,num2)
if self.pair not in CACHED_VALUES.keys():
print('Calculating from init...')
CACHED_VALUES[self.pair] = self.sum
else:
print('Already stored... Getting the values from cache...')
@property
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
t1 = SomeClass(2,3)
print(t1.sum)
t2 = SomeClass(2,3)
print(t2.sum)
print('This is new instance.')
t3 = SomeClass(2,3)
print(t3.sum)
首先,我创建了 CACHED_VALUES
空字典(暂时)。请注意,它是在 class 之外声明的。
其次,我创建了 self.pair
变量,该变量将两个数字表示为字符串,以逗号分隔。这样做的原因是因为您 不能将 列表作为 字典键 。这就是我们将两个数字连接成字符串的原因。
如果我们应用这种方法,CACHED_VALUES
字典将更新如下:
CACHED_VALUES = {}
t1 = SomeClass(2,3)
print(CACHED_VALUES)
>> {'2,3': 5}
现在介绍 __init__
方法。
我添加了 if 条件来检查 CACHED_VALUES
字典是否已经包含计算值。如果不是 - 执行函数并将返回值保存到字典中。如果它存在 - 那么我们得到已经计算的值,省略函数执行。
您可以在下面看到修改后的代码及其输出:
CACHED_VALUES = {}
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
self.pair = '{},{}'.format(num1,num2)
if self.pair not in CACHED_VALUES.keys():
print('Calculating from init...')
CACHED_VALUES[self.pair] = self.sum
else:
print('Already stored... Getting the values from cache...')
@property
def sum(self): #this can even be a class method if needed, I don't really care
return self.num1 + self.num2
print('[X]Creating first instance')
t1 = SomeClass(2,3)
print(t1.sum)
print('[X]Creating second instance')
t2 = SomeClass(2,3)
print(t2.sum)
print('[X]This is instance with different values.')
t3 = SomeClass(5,7)
print(t3.sum)
print('[X]This is second instance with different values.')
t4 = SomeClass(5,7)
print(t4.sum)
# OUTPUT:
[X]Creating first instance
Calculating from init...
5
[X]Creating second instance
Already stored... Getting the values from cache...
5
[X]This is instance with different values.
Calculating from init...
12
[X]This is second instance with same values.
Already stored... Getting the values from cache...
12
所以我找到了一个解决方案,虽然不是使用 funtools.lru_cache
,而是 ring
。根据我的阅读,lru_cache
无法做到这一点,但如果有人知道使用 lru_cache
的解决方案,请随时 post 它,我会将其标记为正确答案.
import ring
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@ring.dict({})
@property
def sum(self):
return self.num1 + self.num2
我正在添加第二个答案,因为我没有足够的声誉来向您的答案添加评论。
您可以查看以下代码片段(摘自https://docs.python.org/3/library/functools.html):
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
或
@lru_cache(maxsize=32)
def get_pep(num):
'Retrieve text of a Python Enhancement Proposal'
resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
try:
with urllib.request.urlopen(resource) as s:
return s.read()
except urllib.error.HTTPError:
return 'Not Found'
>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
... pep = get_pep(n)
... print(n, len(pep))
>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
使用methodtools.lru_cache
from methodtools import lru_cache
called = 0
class SomeClass:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
@lru_cache()
@classmethod
def _sum(cls, num1, num2):
global called
called += 1
return num1 + num2
@property
def sum(self):
return self._sum(self.num1, self.num2)
if __name__ == '__main__':
assert called == 0
t1 = SomeClass(2, 3)
print(t1.sum)
assert called == 1
t2 = SomeClass(2, 3)
print(t2.sum)
assert called == 1