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
À partir de Python 3.2, il existe un décorateur intégré:
@functools.lru_cache(maxsize=100, typed=False)
Decorator pour encapsuler une fonction avec un mémoizing callable qui enregistre au maximum les derniers appels. Cela permet de gagner du temps lorsqu'une fonction coûteuse ou liée aux E/S est appelée périodiquement avec les mêmes arguments .
Exemple de cache LRU pour l'informatique Nombres 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)
Si vous êtes bloqué avec Python 2.x, voici une liste d'autres bibliothèques de mémoisation compatibles:
functools32
| PyPI | Code sourcerepoze.lru
| PyPI | Code sourcepylru
| PyPI | Code sourcebackports.functools_lru_cache
| PyPI | Code sourceOn dirait que vous ne demandez pas à un décorateur de mémoization à usage général (c'est-à-dire que vous n'êtes pas intéressé par le cas général où vous souhaitez mettre en cache renvoyer des valeurs pour différentes valeurs d'argument). C'est-à-dire que vous aimeriez avoir ceci:
x = obj.name # expensive
y = obj.name # cheap
alors qu'un décorateur de mémoization à usage général vous donnerait ceci:
x = obj.name() # expensive
y = obj.name() # cheap
Je soumets que la syntaxe d'appel de méthode est un meilleur style, car elle suggère la possibilité d'un calcul coûteux, tandis que la syntaxe de propriété suggère une recherche rapide.
[Mise à jour: le décorateur de mémoization basé sur la classe que j'avais lié et cité précédemment ne fonctionne pas pour les méthodes. Je l'ai remplacée par une fonction de décorateur.] Si vous souhaitez utiliser un décorateur de mémoization à usage général, en voici un 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 décorateur de mémo avec une limite de taille de cache peut être trouvé ici .
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'}
J'ai codé cette classe de décorateur simple pour mettre en cache les réponses des fonctions. Je trouve cela TRES utile pour mes projets:
from datetime import datetime, timedelta
class cached(object):
def __init__(self, *args, **kwargs):
self.cached_function_responses = {}
self.default_max_age = kwargs.get("default_cache_max_age", timedelta(seconds=0))
def __call__(self, func):
def inner(*args, **kwargs):
max_age = kwargs.get('max_age', self.default_max_age)
if not max_age or func not in self.cached_function_responses or (datetime.now() - self.cached_function_responses[func]['fetch_time'] > max_age):
if 'max_age' in kwargs: del kwargs['max_age']
res = func(*args, **kwargs)
self.cached_function_responses[func] = {'data': res, 'fetch_time': datetime.now()}
return self.cached_function_responses[func]['data']
return inner
L'utilisation est simple:
import time
@cached
def myfunc(a):
print "in func"
return (a, datetime.now())
@cached(default_max_age = timedelta(seconds=6))
def cacheable_test(a):
print "in cacheable test: "
return (a, datetime.now())
print cacheable_test(1,max_age=timedelta(seconds=5))
print cacheable_test(2,max_age=timedelta(seconds=5))
time.sleep(7)
print cacheable_test(3,max_age=timedelta(seconds=5))
DISCLAIMER: Je suis l'auteur de kids.cache .
Vous devriez vérifier kids.cache
, il fournit un décorateur @cache
qui fonctionne sur python 2 et python 3. Aucune dépendance, ~ 100 lignes de code. C'est très simple à utiliser, par exemple, avec votre code en tête, vous pourriez l'utiliser comme ceci:
pip install kids.cache
Ensuite
from kids.cache import cache
...
class MyClass(object):
...
@cache # <-- That's all you need to do
@property
def name(self):
return 1 + 1 # supposedly expensive calculation
Ou vous pouvez mettre le décorateur @cache
après le @property
(même résultat).
L'utilisation du cache sur une propriété s'appelle lazy evaluation _, kids.cache
peut faire beaucoup plus (cela fonctionne avec function avec n'importe quels arguments, propriétés, n'importe quel type de méthodes et même de classes ...). Pour les utilisateurs expérimentés, kids.cache
prend en charge cachetools
, qui fournit des mémoires de cache fantaisie pour python 2 et python 3 (cache LRU, LFU, TTL, RR).
NOTE IMPORTANTE: le cache par défaut de kids.cache
est un dict standard, ce qui n'est pas recommandé pour les programmes de longue durée avec des requêtes toujours différentes, car cela conduirait à un stockage en cache de plus en plus important. Pour cet usage, vous pouvez connecter d'autres mémoires cache en utilisant par exemple (@cache(use=cachetools.LRUCache(maxsize=2))
pour décorer votre fonction/propriété/classe/méthode ...).
Ah, je voulais juste trouver le bon nom pour ceci: " Évaluation de propriété paresseuse ".
Je fais beaucoup cela aussi; peut-être que j'utiliserai cette recette dans mon code un jour.
Si vous utilisez Framework Django, il dispose d'une telle propriété pour mettre en cache une vue ou une réponse de l'API À l'aide de @cache_page(time)
.
Exemple:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
Plus de détails peuvent être trouvés ici .
Il existe encore un autre exemple de décorateur memoize sur Python Wiki:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
Cet exemple est un peu intelligent, car il ne mettra pas les résultats en cache si les paramètres sont mutables. (vérifiez ce code, c'est très simple et intéressant!)
Il y a fastcache , qui est "Implémentation C de functools.lru_cache de Python 3. Fournit une accélération de 10-30x par rapport à la bibliothèque standard."
Identique à réponse choisie , juste une importation différente:
from fastcache import lru_cache
@lru_cache(maxsize=128, typed=False)
def f(a, b):
pass
En outre, il est installé dans Anaconda , contrairement à functools pour lequel doit être installé .
@lru_cache
n'est pas parfait avec les valeurs de fonction par défaut
mon décorateur mem
:
import inspect
def get_default_args(f):
signature = inspect.signature(f)
return {
k: v.default
for k, v in signature.parameters.items()
if v.default is not inspect.Parameter.empty
}
def full_kwargs(f, kwargs):
res = dict(get_default_args(f))
res.update(kwargs)
return res
def mem(func):
cache = dict()
def wrapper(*args, **kwargs):
kwargs = full_kwargs(func, kwargs)
key = list(args)
key.extend(kwargs.values())
key = hash(Tuple(key))
if key in cache:
return cache[key]
else:
res = func(*args, **kwargs)
cache[key] = res
return res
return wrapper
et code pour tester:
from time import sleep
@mem
def count(a, *x, z=10):
sleep(2)
x = list(x)
x.append(z)
x.append(a)
return sum(x)
def main():
print(count(1,2,3,4,5))
print(count(1,2,3,4,5))
print(count(1,2,3,4,5, z=6))
print(count(1,2,3,4,5, z=6))
print(count(1))
print(count(1, z=10))
if __== '__main__':
main()
résultat - seulement 3 fois avec sommeil
mais avec @lru_cache
ce sera 4 fois, parce que ceci:
print(count(1))
print(count(1, z=10))
sera calculé deux fois (mauvais travail avec les valeurs par défaut)
J'ai implémenté quelque chose comme ceci, en utilisant pickle pour la persistance et en utilisant sha1 comme identifiants courts, presque certainement uniques. Fondamentalement, le cache hachait le code de la fonction et l'historique des arguments pour obtenir un sha1, puis recherchait un fichier avec ce sha1 dans le nom. S'il existait, il l'ouvrait et renvoyait le résultat. sinon, il appelle la fonction et enregistre le résultat (éventuellement uniquement si le traitement a pris un certain temps).
Cela dit, je jurerais avoir trouvé un module existant qui a fait cela et je me trouve ici en train de trouver ce module ... Le plus proche que je puisse trouver est le suivant: http: // chase-seibert. github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html
Le seul problème que je vois avec cela est que cela ne fonctionnerait pas bien pour les entrées de grande taille car il hache str (arg), ce qui n'est pas unique pour les tableaux géants.
Ce serait bien s'il y avait un protocole unique_hash () dans lequel une classe renvoie un hachage sécurisé de son contenu. En gros, j'ai implémenté cela manuellement pour les types qui me tenaient à cœur.
Essayez joblib http://pythonhosted.org/joblib/memory.html
from joblib import Memory
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
def f(x):
print('Running f(%s)' % x)
return x
Si vous utilisez Django et souhaitez mettre en cache des vues, voir Réponse de Nikhil Kumar .
Mais si vous voulez mettre en cache TOUT résultat de fonction, vous pouvez utiliser Django-cache-utils .
Il réutilise les caches Django et fournit un décorateur cached
facile à utiliser:
from cache_utils.decorators import cached
@cached(60)
def foo(x, y=0):
print 'foo is called'
return x+y
Python 3.8 cached_property
décorateur
https://docs.python.org/dev/library/functools.html#functools.cached_property
cached_property
a été mentionné à: https://stackoverflow.com/a/5295190/895245 mais il sera fusionné en 3.8, ce qui est génial.
Ce décorateur peut être vu en tant que cache @property
ou en tant que nettoyeur @functools.lru_cache
lorsque vous n’avez pas d’argument.
Les docs disent:
@functools.cached_property(func)
Transformez une méthode d'une classe en une propriété dont la valeur est calculée une fois, puis mise en cache en tant qu'attribut normal pour la durée de vie de l'instance. Similaire à property (), avec l'ajout de la mise en cache. Utile pour les propriétés coûteuses d'instances qui sont autrement immuables.
Exemple:
class DataSet: def __init__(self, sequence_of_numbers): self._data = sequence_of_numbers @cached_property def stdev(self): return statistics.stdev(self._data) @cached_property def variance(self): return statistics.variance(self._data)
Nouveau dans la version 3.8.
Remarque Ce décorateur nécessite que l'attribut dict de chaque instance soit un mappage mutable. Cela signifie que cela ne fonctionnera pas avec certains types, tels que les métaclasses (puisque les attributs dict des instances de type sont des proxys en lecture seule pour l’espace de nommage de la classe), et ceux spécifiant slots sans y compris dict comme l’un des créneaux définis (en tant que telles classes ne fournissent aucun attribut dict ).