为所有类实例保存的lru_cache

时间:2018-10-24 10:55:56

标签: python-3.x class caching properties functools

有什么办法可以在python的类级别上let _ = MenuButton(parent: view, x: -20, y: 20, width: 40, height: 40) lru_cache,以便即使在另一个类实例返回具有相同值的计算属性时,也不会重新计算该属性,而是从缓存中拉出。我想实现以下目标:

@property

4 个答案:

答案 0 :(得分:2)

所以我找到了一个解决方案,尽管没有使用funtools.lru_cache,而是使用ring。根据我的阅读,lru_cache无法做到这一点,但是如果有人知道使用lru_cache的解决方案,请随时发布,并将其标记为正确答案。

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

答案 1 :(得分:1)

我认为可以使用变量保存已经执行的计算来完成。 我对您的代码做了一些修改,让我们来看一下。 考虑您具有以下代码:

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空字典(目前)。请注意,它在类之外声明。 其次,我创建了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

答案 2 :(得分:1)

我要添加第二个答案,因为我没有足够的声誉在您的答案中添加评论。

您可以检查以下代码段(摘自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)

答案 3 :(得分:1)

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