Hacer que @lru_cache ignore algunos de los argumentos de la función

3 minutos de lectura

Avatar de usuario de WGH
WGH

como puedo hacer @functools.lru_cache ¿El decorador ignora algunos de los argumentos de la función con respecto a la clave de almacenamiento en caché?

Por ejemplo, tengo una función que se ve así:

def find_object(db_handle, query):
    # (omitted code)
    return result

si aplico lru_cache decorador así, db_handle se incluirán en la clave de caché. Como resultado, si intento llamar a la función con el mismo querypero diferente db_handle, se ejecutará nuevamente, lo que me gustaría evitar. Quiero lru_cache considerar query argumento solamente.

Avatar de usuario de Yann
yan

Con cachetools puedes escribir:

from cachetools import cached
from cachetools.keys import hashkey

from random import randint

@cached(cache={}, key=lambda db_handle, query: hashkey(query))
def find_object(db_handle, query):
    print("processing {0}".format(query))
    return query

queries = list(range(5))
queries.extend(range(5))
for q in queries:
    print("result: {0}".format(find_object(randint(0, 1000), q)))

Deberá instalar cachetools (pip install cachetools).

La sintaxis es:

@cached(
    cache={},
    key=lambda <all-function-args>: hashkey(<relevant-args>)
)

Aquí hay otro ejemplo que incluye argumentos de palabras clave:

@cached(
    cache={},
    key=lambda a, b, c=1, d=2: hashkey(a, c)
)
def my_func(a, b, c=1, d=2):
    return a + c

En el ejemplo anterior, tenga en cuenta que los argumentos de entrada de la función lambda coinciden con el my_func argumentos No es necesario que coincida exactamente con argspec si no es necesario. Por ejemplo, puede usar kwargs para eliminar cosas que no son necesarias en el hashkey:

@cached(
    cache={},
    key=lambda a, b, c=1, **kwargs: hashkey(a, c)
)
def my_func(a, b, c=1, d=2, e=3, f=4):
    return a + c

En el ejemplo anterior no nos importa d=, e= y f= args al buscar un valor de caché, por lo que podemos aplastarlos con **kwargs.

  • ¿Puede dar más detalles sobre esta respuesta con qué clave y hashkey son?

    – tommy

    19 de noviembre de 2021 a las 1:32

  • Para cualquier otra persona que desee una respuesta al comentario de @Tommy, pude encontrar la descripción de key y hashkey en esta sección de la cachetools documentos. Esencialmente, key es una función que devuelve una clave de caché, y hashkey() devuelve una tupla de sus argumentos como una clave de caché interna y verifica que cada argumento se pueda modificar.

    – Homero Simpson

    16 de febrero de 2022 a las 22:11

  • ¿Qué imprime esto? Imprime: processing 0\n result: 0\n processing 1\n result: 1\n processing 2\n result: 2\n processing 3\n result: 3\n processing 4\n result: 4\n result: 0\n result: 1\n result: 2\n result: 3\n result: 4\n ???

    – joseville

    24 de febrero de 2022 a las 18:11


  • @Tommy, edité la respuesta para incluir más explicaciones sobre la sintaxis necesaria. Espero que esto ayude.

    – JGC

    24 de marzo de 2022 a las 16:44

  • en general para la función def function(a,b): donde solo se utilizará b como clave de caché, haga @cached(cache={}, key=lambda a,b: hashkey(b)) (la sintaxis es confusa a primera vista)

    – stam

    10 de febrero a las 17:58

Tengo al menos una solución muy fea. Envoltura db_handle en un objeto que siempre es igual, y desenvuélvelo dentro de la función.

Requiere un decorador con bastantes funciones de ayuda, lo que hace que el seguimiento de la pila sea bastante confuso.

class _Equals(object):
    def __init__(self, o):
        self.obj = o

    def __eq__(self, other):
        return True

    def __hash__(self):
        return 0

def lru_cache_ignoring_first_argument(*args, **kwargs):
    lru_decorator = functools.lru_cache(*args, **kwargs)

    def decorator(f):
        @lru_decorator
        def helper(arg1, *args, **kwargs):
            arg1 = arg1.obj
            return f(arg1, *args, **kwargs)

        @functools.wraps(f)
        def function(arg1, *args, **kwargs):
            arg1 = _Equals(arg1)
            return helper(arg1, *args, **kwargs)

        return function

    return decorator

  • Estoy reviviendo esta respuesta muy antigua, pero ¿cómo usarías el lru_cache_ignoring_first_argument ¿decorador?

    – Sanandrea

    20 dic 2022 a las 17:00

¿Ha sido útil esta solución?