Décorateur de Cache Python LRU par Instance

Utilisation du décorateur de cache LRU trouvé ici: http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/

from lru_cache import lru_cache
class Test:
    @lru_cache(maxsize=16)
    def cached_method(self, x):
         return x + 5

Je peux créer une méthode de classe décorée avec ceci mais elle finit par créer un cache global qui s'applique à Toutes les instances de test de classe. Cependant, mon intention était de créer un par exemple cache. Donc, si je devais instancier 3 Tests, j'aurais 3 caches LRU plutôt que 1 cache LRU pour toutes les 3 instances.

Le la seule indication que j'ai que cela se produit est lors de l'appel de cache_info() sur les différentes méthodes décorées par les instances de classe, elles renvoient toutes les mêmes statistiques de cache (ce qui est extrêmement peu probable étant donné qu'elles interagissent avec des arguments très différents):

CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)
CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)
CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)

Existe-t-il un décorateur ou une astuce qui me permettrait de créer facilement un cache pour chaque instance de classe?

26
demandé sur crzysdrs 2013-02-19 02:12:57

2 réponses

En supposant que vous ne voulez pas modifier le code (par exemple, parce que vous voulez pouvoir juste porter à 3.3 et utiliser le stdlib functools.lru_cache, ou utiliser functools32 de PyPI au lieu de copier et coller une recette dans votre code), il y a une solution évidente: créer une nouvelle méthode d'instance décorée avec chaque instance.

class Test:
    def cached_method(self, x):
         return x + 5
    def __init__(self):
         self.cached_method = lru_cache(maxsize=16)(self.cached_method)
32
répondu abarnert 2013-02-18 22:31:16

Que diriez-vous de ceci: un décorateur function qui enveloppe la méthode avec lru_cache la première fois qu'elle est appelée sur chaque instance?

def instance_method_lru_cache(*cache_args, **cache_kwargs):
    def cache_decorator(func):
        @wraps(func)
        def cache_factory(self, *args, **kwargs):
            print('creating cache')
            instance_cache = lru_cache(*cache_args, **cache_kwargs)(func)
            instance_cache = instance_cache.__get__(self, self.__class__)
            setattr(self, func.__name__, instance_cache)
            return instance_cache(*args, **kwargs)
        return cache_factory
    return cache_decorator

Utilisez-le comme ceci:

class Foo:
    @instance_method_lru_cache()
    def times_2(self, bar):
        return bar * 2

foo1 = Foo()
foo2 = Foo()

print(foo1.times_2(2))
# creating cache
# 4
foo1.times_2(2)
# 4

print(foo2.times_2(2))
# creating cache
# 4
foo2.times_2(2)
# 4

Voici un aperçu de GitHub avec une documentation en ligne.

2
répondu z0r 2016-09-02 14:47:45