让@lru_cache忽略一些函数参数

问题描述 投票:0回答:3

如何使

@functools.lru_cache
装饰器忽略一些与缓存键有关的函数参数?

例如,我有一个如下所示的函数:

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

如果我像这样应用

lru_cache
装饰器,
db_handle
将包含在缓存键中。因此,如果我尝试使用相同的
query
但不同的
db_handle
调用该函数,它将再次执行,这是我想避免的。我希望
lru_cache
仅考虑
query
论点。

python caching python-decorators lru functools
3个回答
80
投票

使用 cachetools 你可以写:

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)))

您需要安装缓存工具(

pip install cachetools
)。

语法是:

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

这是另一个包含关键字参数的示例:

@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

在上面的示例中,请注意 lambda 函数输入参数与

my_func
参数匹配。如果不需要,则不必完全匹配 argspec。例如,您可以使用 kwargs 来消除 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

在上面的示例中,我们在查找缓存值时并不关心

d=
e=
f=
参数,因此我们可以使用 **kwargs 将它们全部压缩掉。


14
投票

我至少有一个非常丑陋的解决方案。将

db_handle
包装在始终相等的对象中,然后在函数内展开它。

它需要一个具有相当多辅助函数的装饰器,这使得堆栈跟踪非常混乱。

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

0
投票

我希望

lru_cache
仅考虑
query
论点。

lru_cache
应用于仅采用查询参数的辅助函数。这样,缓存只能看到一个相关参数,即唯一标识单个缓存结果的参数。

接下来,编写一个接受两个参数的新函数,将第一个参数保存在辅助函数可以访问的位置,并使用第二个参数直接调用辅助函数:

def original_function(db_handle, query):
    "Unmodified original function except for its name"
    result = len(query)                # Simulate a computation  
    print('Running', query, 'with', db_handle, 'giving', result)
    return result

@lru_cache
def auxiliary(query):
    db_handle = auxiliary.db_handle    
    return original_function(db_handle, query)

def new_function(db_handle, query):
    auxiliary.db_handle = db_handle
    return auxiliary(query)

a = find_object('h1', 'first_query')   # Executes new query
b = find_object('h2', 'first_query')   # Cached query
c = find_object('h2', 'second_query')  # Executes new query
© www.soinside.com 2019 - 2024. All rights reserved.