2013-02-18 2 views
23

Использование декоратора LRU кэша здесь: http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/Python LRU Cache декоратор Per Instance

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

Я могу создать декорированный метод класса с этим, но в конечном итоге создание глобального кэша, который относится к всех экземпляров теста класса. Тем не менее, я решил создать кеш для каждого экземпляра. Поэтому, если бы я должен был создать 3 теста, у меня было бы 3 кэша LRU, а не 1 LRU-кеш, что для всех трех экземпляров.

Единственное, что у меня есть, это то, что это происходит при вызове cache_info() для различных методов оформления экземпляров класса, все они возвращают одну и ту же статистику кеша (что крайне маловероятно, если они взаимодействуют с очень разными аргументы):

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) 

есть декоратор или трюк, который позволит мне легко вызвать этот декоратор, чтобы создать кэш для каждого экземпляра класса?

+1

Помните, что декоратор - это просто синтаксический сахар для 'def method: pass; method = decorate (method) '. Таким образом, вы можете механически перевести это, чтобы создать оформленный метод в вашем '__init__'. –

+0

Вы уверены, что знаете, что такое метод класса? Потому что я думаю, что вы ищете нормальный метод. Если метод класса специфичен для экземпляра, он по определению является обычным методом экземпляра. Или почему именно вам нужен метод класса? Или почему вам нужен кеш «на экземпляр»? – Mayou36

ответ

28

Предполагая, что вы не хотите, чтобы изменить код (например, потому что вы хотите, чтобы иметь возможность просто порт 3.3 и использовать STDLIB functools.lru_cache, или использовать functools32 из PyPI вместо копирования и вставки рецепт в код), есть одно очевидное решение: Создайте новый декорированный экземпляр с каждым экземпляром.

class Test: 
    def cached_method(self, x): 
     return x + 5 
    def __init__(self): 
     self.cached_method = lru_cache(maxsize=16)(self.cached_method) 
+1

Удивительный. Но как я могу это сделать для get-only @property? – Gilly

2

Как об этом: в функции декоратора, который оборачивает метод с lru_cache в первый раз это называется на каждом экземпляре?

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 

Используйте это так:

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 

Here's a gist on GitHub с некоторой инлайн документации.

Смежные вопросы