2015-11-22 1 views
0

Для memoize f(x) можно использовать functools.lru_cache():Как memoize метод, когда один из его memoized параметров должен быть экземпляр переменной

class A(object): 

    def __init(self): 
     self.time = 10 # This changes in the various spots in the program 

    @functools.lru_cache(maxsize=None) 
    def f(self, x): 
     # Lots of code 
     # ... 
     # ... 
     return x * some_other_func(self.time) 

(в моем понимании) lru_cache() создает Dict с различными x как ключи и соответствующие им f(x) как значения, так что он возвращает сохраненные значения, если я вызываю f() с тем же значением аргумента, а не пересчитываю его. Однако это не то, что мне нужно.

Моя цель состоит в том, чтобы memoize f() значения для различных значений какxиself.time.


Используя следующий код достигает своей цели:

class A(object): 

    def __init(self): 
     self.time = 10 

    @functools.lru_cache(maxsize=None) 
    def g(self, x, t): 
     # Lots of code 
     # ... 
     # ... 
     return x * some_other_func(self.time) 

    def f(self, x): 
     return self.g(x=x, t=self.time) 

Теперь вместо memoizing f(x) непосредственно, я memoize g(), который всегда вызывается с t=self.time.

Однако я не уверен, является ли это самым чистым решением. Я ожидал бы использовать декоратор для memoization и не создавать промежуточные методы.

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

+0

Даже если 'self' был в области при настройке аргументов по умолчанию, значения по умолчанию игнорируются кешированием. Либо придерживайтесь того, что у вас есть, либо реализуйте кеш самостоятельно. – jonrsharpe

+0

@jonrsharpe Прошу прощения, это не то, что я имел в виду. Я удалил его с поста. Мне нужно просто принять во внимание переменную, которая не является частью параметров 'f()', но не знает, как это объяснить. –

+0

Ваше решение кажется прекрасным для меня – yurib

ответ

1

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

from functools import wraps 

def stateful_memoize(*attrs): 
    """Memoization that respects specified instance state.""" 
    def decorator(method): 
     @wraps(method) 
     def wrapper(self, *args): 
      state = args + tuple(getattr(self, attr) 
           for attr in attrs) 
      if state not in wrapper.cache: 
       wrapper.cache[state] = method(self, *args) 
      return wrapper.cache[state] 
     wrapper.cache = {} 
     return wrapper 
    return decorator 

Эта простая версия не будет работать с аргументами метода ключевого слова, но в остальном это должно быть хорошо. При использовании:

>>> class A(object): 

    def __init__(self): 
     self.time = 10 

    @stateful_memoize('time') 
    def f(self, x): 
     print('Calling f with x={!r}, self.time={!r}'.format(x, self.time)) 
     return x * self.time 

>>> a = A() 
>>> a.f(1) 
Calling f with x=1, self.time=10 
10 
>>> a.f(1) 
10 
>>> a.time = 5 
>>> a.f(1) 
Calling f with x=1, self.time=5 
5 
>>> a.time = 10 
>>> a.f(1) 
10 
>>> a.f(2) 
Calling f with x=2, self.time=10 
20 
>>> a.f.cache 
{(1, 10): 10, (1, 5): 5, (2, 10): 20}