为类和静态方法配置lru_cache

时间:2017-10-03 12:44:58

标签: python python-3.x lru

我正在尝试在Python3中使用lru_cache来加速对Salesforce数据库的常见查询。以下是应该

的相关代码
  • a)将不可散列的参数转换为可散列的参数,并
  • b)为这些对象启用LRU缓存。

当我尝试这段代码时,缓存可以用于调用没有参数的函数,但它似乎并没有用参数缓存函数调用。另外,我不知道如何为装饰函数订购装饰器。

注意,我在这里使用类和静态方法的类,因此我可以覆盖get的不同子类的get_allResource方法。

请解释我做错了什么或者做得更好。

from functools import lru_cache
from functools import wraps

class Resource(object):

    def hash_dict(func):
        """Transform mutable dictionnary
           Into immutable
           Useful to be compatible with cache
        """
        class HDict(dict):
            def __hash__(self):
                return hash(frozenset(self.items()))

        @wraps(func)
        def wrapped(*args, **kwargs):
            args = tuple([HDict(arg) if isinstance(arg, dict) else arg for arg in args])
            kwargs = {}
            for k, v in kwargs.items():
                if isinstance(v, dict):
                    kwargs[k] = HDict(v)
                elif isinstance(v, list):
                    kwargs[k] = tuple(v)
                else:
                    kwargs[k] = v
            return func(*args, **kwargs)
        return wrapped

    @staticmethod
    @hash_dict
    @lru_cache
    def get(cls, resource_id, lang='en', fields=None):
        pass

    @classmethod
    @hash_dict
    @lru_cache
    def get_all(cls, lang='en', filters=None, fields=None):
        pass

3 个答案:

答案 0 :(得分:1)

如果我来不及为你而迟到,那是我的回应。您有几处错误。

首先,您要覆盖args函数定义中的kwargswapped。这实际上是删除您的函数参数。

第二,您只在kwargs情况下创建不可变列表,而不在args情况下。

此外,lru_cache是一个修饰符构造函数,因此必须称为@lru_cache()。没有它,我不知道它如何对您起作用。

此外,您将def get(cls, ...声明为@staticmethod,然后它将不会收到cls参数。

但是最重要的,如this medium article中所述,在类内部定义装饰器并非易事。 我得到您正在尝试做的事情:通过继承来强制进行缓存,尽管这似乎是个好主意,但它无法正常工作。如果您重写了一个函数,则必须再次使用缓存重新对其进行重新装饰,这会错过在类内声明装饰器的意义。

总结一下,我将省去自己的麻烦,并在另一个类中声明装饰器,并在其他地方使用它。但是要小心,因为缓存类方法也不是很简单。


额外:

几周前,我处于类似情况,想要缓存一个接受numpy数组的函数。我根据this implementation提出了this so response。我只是将数组转换为元组,然后再次对其进行重构(因为我需要将其作为最后的可变数组)。

def np_cache(*args, **kwargs):
    """LRU cache implementation for functions whose FIRST parameter is a numpy array
    >>> array = np.array([[1, 2, 3], [4, 5, 6]])
    >>> @np_cache(maxsize=256)
    ... def multiply(array, factor):
    ...     print("Calculating...")
    ...     return factor*array
    >>> multiply(array, 2)
    Calculating...
    array([[ 2,  4,  6],
           [ 8, 10, 12]])
    >>> multiply(array, 2)
    array([[ 2,  4,  6],
           [ 8, 10, 12]])
    >>> multiply.cache_info()
    CacheInfo(hits=1, misses=1, maxsize=256, currsize=1)

    """
    def decorator(function):
        @wraps(function)
        def wrapper(np_array, *args, **kwargs):
            hashable_array = array_to_tuple(np_array)
            return cached_wrapper(hashable_array, *args, **kwargs)

        @lru_cache(*args, **kwargs)
        def cached_wrapper(hashable_array, *args, **kwargs):
            array = np.array(hashable_array)
            return function(array, *args, **kwargs)

        def array_to_tuple(np_array):
            """Iterates recursivelly."""
            try:
                return tuple(array_to_tuple(_) for _ in np_array)
            except TypeError:
                return np_array

        # copy lru_cache attributes over too
        wrapper.cache_info = cached_wrapper.cache_info
        wrapper.cache_clear = cached_wrapper.cache_clear

        return wrapper

    return decorator

虽然这不能直接解决您的问题,但可以很容易地将其推广为任意数量的输入参数。

答案 1 :(得分:1)

这应该是您正在寻找的答案:https://ring-cache.readthedocs.io/en/latest/quickstart.html#method-classmethod-staticmethod

lru_cache仅支持简单功能。 Ring提供了非常相似的界面,但包括任何种类的描述符支持。

class Page(object):
    (...)

    @ring.lru()
    @classmethod
    def class_content(cls):
        return cls.base_content

    @ring.lru()
    @staticmethod
    def example_dot_com():
        return requests.get('http://example.com').content

有关更多详细信息,请参见链接。请注意,该示例不是LRU。

答案 2 :(得分:0)

我不确定您为什么需要从youknowone的答案中获取其他软件包。 我尝试了以下操作,并按预期工作:

import functools


class A:

  @staticmethod
  @functools.lru_cache(maxsize=None)
  def build(value):
    print('Creating', value)
    return A()


assert A.build('a') is A.build('a')
assert A.build('b') is A.build('b')
assert A.build('b') is not A.build('a')