-->

Make @lru_cache ignore some of the function argume

2019-01-23 05:24发布

问题:

How can I make @functools.lru_cache decorator ignore some of the function arguments with regard to caching key?

For example, I have a function that looks like this:

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

If I apply lru_cache decorator just like that, db_handle will be included in the cache key. As a result, if I try to call the function with the same query, but different db_handle, it will be executed again, which I'd like to avoid. I want lru_cache to consider query argument only.

回答1:

With cachetools you can write:

from cachetools import cached, 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)))


回答2:

I have at least one very ugly solution. Wrap db_handle in a object that's always equals, and unwrap it inside the function.

It requires a decorator with quite a bit of helper functions, which makes stack trace quite confusing.

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