AttributeError: 'function' object has no attribute 'cache_info' with functools.lru_cache + argument formatting decorator discrepancy

AttributeError: 'function' object has no attribute 'cache_info' with functools.lru_cache + argument formatting decorator discrepancy

我 运行 在工作中编写了一些代码的简单、人为的示例。我试图更好地理解为什么 slow_function_1 (+它的装饰器的结构方式)会正确缓存函数结果,但应用于 slow_function_2 的装饰器不会。在这个例子中,我试图在调用方法后访问缓存信息;但是,我始终收到以下错误:AttributeError: 'function' object has no attribute 'cache_info'。我到处搜索以尝试解决此问题,但无济于事。 slow_function_1.cache_info()slow_function_2.cache_info()

都会引发此 AttributeError

如何查看函数调用之间的缓存?如果有人对 slow_function_1 和 slow_function_2 缓存行为不同的原始问题有任何见解,我也将不胜感激。

提前致谢!

import functools
import time

def format_args(func):
    def inner(*args, **kwargs):
        formatted_args = [tuple(x) if type(x) == list else x for x in args]
        return func(*formatted_args, **kwargs)
    return inner

def formatted_cache(func):
    def inner(*args, **kwargs):
        formatted_args = [tuple(x) if type(x) == list else x for x in args]
        return functools.lru_cache()(func)(*formatted_args, **kwargs)
    return inner

@format_args
@functools.lru_cache
def slow_function_1(a: list, b: bool):
    time.sleep(1)
    print("executing slow function 1")
    return sum(a)


@formatted_cache
def slow_function_2(a: list, b: bool):
    time.sleep(1)
    print("executing slow function 2")
    return functools.reduce((lambda x, y: x*y), a)


example_list = [1,2,3,4,5,6,7,8,9,10,11,12]
example_bool = True

slow_function_1(example_list, example_bool)
print(slow_function_1.cache_info())
slow_function_1(example_list, example_bool)
print(slow_function_1.cache_info())


slow_function_2(example_list, example_bool)
print(slow_function_2.cache_info())
slow_function_2(example_list, example_bool)
print(slow_function_2.cache_info())

现在我盯着它看了好久,我认为用装饰器做这件事真的不可能。您需要一个 lru_cache 对象来访问缓存和所有这些东西,并且您需要第二个函数在传递给 lru_cache 对象之前将参数格式化为可散列的。装饰器不能同时 return 两者,它们也不能相互嵌套以实现两全其美的功能。

def formatted_cache(func):
    # first we assume func only takes in hashable arguments
    # so cachedfunc only takes in hashable arguments
    cachedfunc = functools.lru_cache(func)
    
    # inner formats lists to hashable tuples
    # then passes it to cachedfunc
    def inner(*args, **kwargs):
        formatted_args = [tuple(x) if type(x) == list else x for x in args]
        return cachedfunc(*formatted_args, **kwargs)
    
    # oh no, we can only return one function, but neither is good enough

我认为前进的唯一方法就是接受这些必须在单独的函数中完成,因为 lru_cache 的限制。实际上并没有那么尴尬,只是一个简单的高阶函数,如 map.

import functools
import time

def formatted_call(func, *args, **kwargs):
    formatted_args = [tuple(x) if type(x) == list else x for x in args]
    return func(*formatted_args, **kwargs)

@functools.lru_cache
def slow_function_2(a: list, b: bool):
    time.sleep(1)
    print("executing slow function 2")
    return functools.reduce((lambda x, y: x*y), a)

example_list = [1,2,3,4,5,6,7,8,9,10,11,12]
example_bool = True

formatted_call(slow_function_2, example_list, example_bool)
print(slow_function_2.cache_info())
formatted_call(slow_function_2, example_list, example_bool)
print(slow_function_2.cache_info())