Кэширование атрибутов классов в Python

Я пишу класс на python, и у меня есть атрибут, который займет относительно много времени для вычисления, поэтому я хочу сделать это только один раз . Кроме того, это не будет необходимо для каждого экземпляра класса, поэтому я не хочу делать это по умолчанию в __init__ .

Я новичок в Python, но не для программирования. Я могу придумать способ сделать это довольно легко, но я снова и снова обнаружил, что способ «что-то делать» Pythonic часто намного проще, чем то, что я придумываю, используя свой опыт на других языках.

Есть ли «правильный» способ сделать это в Python?

6 Solutions collect form web for “Кэширование атрибутов классов в Python”

Обычным способом было бы сделать атрибут свойством и сохранить значение при первом вычислении

 import time class Foo(object): def __init__(self): self._bar = None @property def bar(self): if self._bar is None: print "starting long calculation" time.sleep(5) self._bar = 2*2 print "finished long caclulation" return self._bar foo=Foo() print "Accessing foo.bar" print foo.bar print "Accessing foo.bar" print foo.bar 

Раньше я делал это, как предположил гнибблер, но я в конце концов устал от маленьких шагов по уборке.

Поэтому я построил свой собственный дескриптор:

 class cached_property(object): """ Descriptor (non-data) for building an attribute on-demand on first use. """ def __init__(self, factory): """ <factory> is called such: factory(instance) to build the attribute. """ self._attr_name = factory.__name__ self._factory = factory def __get__(self, instance, owner): # Build the attribute. attr = self._factory(instance) # Cache the value; hide ourselves. setattr(instance, self._attr_name, attr) return attr 

Вот как вы его используете:

 class Spam(object): @cached_property def eggs(self): print 'long calculation here' return 6*2 s = Spam() s.eggs # Calculates the value. s.eggs # Uses cached value. 
  • Python> = 3.2

Вы должны использовать оба @property и @functools.lru_cache :

 import functools class MyClass: @property @functools.lru_cache() def foo(self): print("long calculation here") return 21 * 2 

Этот ответ содержит более подробные примеры, а также упоминает обратный путь для предыдущих версий Python.

  • Python <3.2

В вики Python есть хранитель кешированных свойств (лицензия MIT), которая может быть использована следующим образом:

 import random # the class containing the property must be a new-style class class MyClass(object): # create property whose value is cached for ten minutes @cached_property(ttl=600) def randint(self): # will only be evaluated every 10 min. at maximum. return random.randint(0, 100) 

Или любая реализация, упомянутая в других ответах, соответствует вашим потребностям.
Или вышеупомянутый backport.

 class MemoizeTest: _cache = {} def __init__(self, a): if a in MemoizeTest._cache: self.a = MemoizeTest._cache[a] else: self.a = a**5000 MemoizeTest._cache.update({a:self.a}) 

Вы можете попробовать заглянуть в воспоминания. Способ, которым он работает, заключается в том, что если вы передадите функции с теми же аргументами, она вернет результат кэширования. Более подробную информацию об этом можно найти в python .

Кроме того, в зависимости от того, как настроен ваш код (вы говорите, что он не нужен всем экземплярам), вы можете попытаться использовать какой-то мухомодный шаблон или ленивую загрузку.

Самый простой способ сделать это, вероятно, состоял бы в том, чтобы просто написать метод (вместо использования атрибута), который обтекает атрибут (метод getter). При первом вызове этот метод вычисляет, сохраняет и возвращает значение; позже он просто возвращает сохраненное значение.

Python - лучший язык программирования в мире.