retorno - retornar valores de una funcion python
¿Hay un decorador para simplemente almacenar en caché los valores de retorno de función? (13)
Considera lo siguiente:
@property
def name(self):
if not hasattr(self, ''_name''):
# expensive calculation
self._name = 1 + 1
return self._name
Soy nuevo, pero creo que el almacenamiento en caché podría tenerse en cuenta en un decorador. Solo que no encontré uno como este;)
PD: el cálculo real no depende de valores variables
A partir de Python 3.2 hay un decorador incorporado:
@functools.lru_cache(maxsize=100, typed=False)
Decorador para ajustar una función con un memorable llamativo que ahorra hasta el máximo de llamadas más recientes. Puede ahorrar tiempo cuando se llama periódicamente una función costosa o vinculada a E / S con los mismos argumentos.
Ejemplo de un caché de LRU para calcular los números 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)
Si está atrapado con Python 2.x, aquí hay una lista de otras bibliotecas de memoialización compatibles:
-
functools32
| PyPI | Código fuente -
repoze.lru
| PyPI | Código fuente -
pylru
| PyPI | Código fuente -
backports.functools_lru_cache
| PyPI | Código fuente
Ah, solo necesitaba encontrar el nombre correcto para esto: " evaluación de propiedades perezosas ".
Yo hago esto mucho también; tal vez use esa receta en mi código en algún momento.
Codifiqué esta simple clase de decorador para almacenar en caché las respuestas de la función. Lo encuentro MUY útil para mis proyectos:
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
El uso es directo:
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))
Hay otro ejemplo más de decorador de memoizes en Python Wiki:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
Ese ejemplo es un poco inteligente, porque no guardará en caché los resultados si los parámetros son mutables. (¡revisa ese código, es muy simple e interesante!)
Implementé algo como esto, usando pickle for persistance y usando sha1 para identificaciones cortas casi ciertamente únicas. Básicamente, el caché cifró el código de la función y la hist de argumentos para obtener un sha1 y luego buscó un archivo con ese sha1 en el nombre. Si existió, lo abrió y devolvió el resultado; si no, llama a la función y guarda el resultado (opcionalmente solo guardando si tardó un cierto tiempo en procesarse).
Dicho esto, juraría que encontré un módulo existente que hizo esto y me encontré aquí tratando de encontrar ese módulo ... Lo más cercano que puedo encontrar es esto, que se ve bien: http://chase-seibert.github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html
El único problema que veo con eso es que no funcionaría bien para grandes entradas dado que tiene hashes str (arg), que no es exclusivo para arreglos gigantes.
Sería bueno si hubiera un protocolo unique_hash () que tuviera una clase que devuelva un hash seguro de su contenido. Básicamente lo implementé manualmente para los tipos que me importaban.
Junto con el http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize encontré los siguientes paquetes de Python:
Parece que no está pidiendo un decorador de memoria de propósito general (es decir, no está interesado en el caso general en el que desea almacenar en caché los valores devueltos para diferentes valores de argumento). Es decir, le gustaría tener esto:
x = obj.name # expensive
y = obj.name # cheap
mientras que un decorador de memorias de propósito general le daría esto:
x = obj.name() # expensive
y = obj.name() # cheap
Presento que la sintaxis método-llamada es un mejor estilo, ya que sugiere la posibilidad de un cálculo costoso, mientras que la sintaxis de la propiedad sugiere una búsqueda rápida.
[Actualización: el decorador de memoraciones basado en la clase al que había vinculado y citado anteriormente no funciona para los métodos. Lo he reemplazado con una función de decorador.] Si está dispuesto a usar un decorador de memoria de propósito general, aquí hay uno 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
Ejemplo de uso:
@memoize
def fibonacci(n):
if n < 2: return n
return fibonacci(n - 1) + fibonacci(n - 2)
Otro decorador de memorias con un límite en el tamaño de la caché se puede encontrar here .
Prueba 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 está usando Django y desea almacenar vistas en caché, consulte la respuesta de Nikhil Kumar .
Pero si quiere almacenar en caché CUALQUIER resultado de la función, puede usar django-cache-utils .
Reutiliza los cachés de Django y proporciona un decorador en cached
fácil de usar:
from cache_utils.decorators import cached
@cached(60)
def foo(x, y=0):
print ''foo is called''
return x+y
Si está utilizando Django Framework, tiene una propiedad para almacenar en caché una vista o respuesta de API usando @cache_page(time)
y también puede haber otras opciones.
Ejemplo:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
Más detalles se pueden encontrar here .
DESCARGO DE RESPONSABILIDAD: soy el autor de kids.cache .
Debería comprobar kids.cache
, proporciona un decorador @cache
que funciona en python 2 y python 3. Sin dependencias, ~ 100 líneas de código. Es muy fácil de usar, por ejemplo, con su código en mente, podría usarlo así:
pip install kids.cache
Entonces
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
O puede poner el decorador @cache
después de @property
(mismo resultado).
Usar el caché en una propiedad se llama evaluación diferida , kids.cache
puede hacer mucho más (funciona en función de cualquier argumento, propiedad, cualquier tipo de método e incluso clases ...). Para los usuarios avanzados, kids.cache
compatible con cachetools
que proporciona sofisticadas tiendas de caché para python 2 y python 3 (LRU, LFU, TTL, caché RR).
NOTA IMPORTANTE : el almacén de caché predeterminado de kids.cache
es un dict estándar, que no se recomienda para el programa de ejecución prolongada con consultas diferentes, ya que daría lugar a una tienda de almacenamiento en caché cada vez mayor. Para este uso, puede agregar otras tiendas de caché utilizando, por ejemplo, ( @cache(use=cachetools.LRUCache(maxsize=2))
para decorar su función / propiedad / clase / método ...
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
Usos de muestra:
>>> @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''}