187 votes

Existe-t-il un décorateur pour simplement mettre en cache les valeurs de retour des fonctions?

Considérer ce qui suit:

 @property
def name(self):

    if not hasattr(self, '_name'):

    	# expensive calculation
    	self._name = 1 + 1

    return self._name
 

Je suis nouveau, mais je pense que la mise en cache pourrait être prise en compte dans un décorateur. Seulement je n'en ai pas trouvé un pareil;)

PS le vrai calcul ne dépend pas des valeurs mutables

229voto

Paolo Moretti Points 9519

À partir de Python 3.2, il est intégré dans décorateur:

@functools.lru_cache(maxsize=100, typed=False)

Décorateur d'enrouler une fonction avec un memoizing appelable qui permet d'économiser jusqu'à la maxsize appels les plus récents. Il peut gagner du temps lors d'un coûteux ou I/O bound fonction est régulièrement appelé avec les mêmes arguments.

Exemple d'un cache LRU pour le calcul des nombres de Fibonacci:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

33voto

Nathan Kitchen Points 2729

Il semble que vous êtes de ne pas demander pour un usage général memoization décorateur (c'est à dire, vous n'êtes pas intéressé, dans le cas général où vous souhaitez mettre en cache les valeurs de retour pour différentes valeurs d'argument). Qui est, vous aimeriez avoir ceci:

x = obj.name  # expensive
y = obj.name  # cheap

bien que d'un usage général memoization décorateur vous donnerait ceci:

x = obj.name()  # expensive
y = obj.name()  # cheap

Je pense que la méthode-la syntaxe d'appel est mieux de style, car elle suggère la possibilité d'un calcul coûteux alors que la syntaxe de la propriété suggère une recherche rapide.

[Mise à jour: La classe de base de memoization décorateur, j'ai eu liés et cités précédemment ne fonctionne pas pour les méthodes. Je l'ai remplacé par un décorateur fonction.] Si vous êtes prêt à utiliser un memoization décorateur, voici un exemple simple:

def memoize(function):
  memo = {}
  def wrapper(*args):
    if args in memo:
      return memo[args]
    else:
      rv = function(*args)
      memo[args] = rv
      return rv
  return wrapper

Exemple d'utilisation:

@memoize
def fibonacci(n):
  if n < 2: return n
  return fibonacci(n - 1) + fibonacci(n - 2)

Un autre memoization décorateur avec une limite sur la taille du cache peut être trouvé ici.

24voto

acmerfight Points 31
 class memorize(dict):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        return self[args]

    def __missing__(self, key):
        result = self[key] = self.func(*key)
        return result
 

Exemples d'utilisations:

 >>> @memorize
... def foo(a, b):
...     return a * b
>>> foo(2, 4)
8
>>> foo
{(2, 4): 8}
>>> foo('hi', 3)
'hihihi'
>>> foo
{(2, 4): 8, ('hi', 3): 'hihihi'}
 

20voto

Alex Martelli Points 330805

Il existe des décorateurs mémoizing qui effectuent ce que vous appelez la "mise en cache", par exemple http://snippets.dzone.com/posts/show/4840 - ils travaillent généralement sur des fonctions en tant que telles (qu'elles soient destinées à devenir des méthodes ou non) dont les résultats dépendent sur leurs arguments (pas sur des choses mutables comme self! -) et ainsi garder un mémo-dict séparé.

11voto

Imran Points 20117

Werkzeug a un décorateur cached_property ( docs , source )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X