Sus*_*sio 9 python caching numpy decorator python-3.x
我正在尝试为具有 numpy 数组输入参数的函数制作一个缓存装饰器
from functools import lru_cache
import numpy as np
from time import sleep
a = np.array([1,2,3,4])
@lru_cache()
def square(array):
sleep(1)
return array * array
square(a)
Run Code Online (Sandbox Code Playgroud)
但是 numpy 数组不可散列,
TypeError Traceback (most recent call last)
<ipython-input-13-559f69d0dec3> in <module>()
----> 1 square(a)
TypeError: unhashable type: 'numpy.ndarray'
Run Code Online (Sandbox Code Playgroud)
所以它们需要转换为元组。我有这个工作和缓存正确:
@lru_cache()
def square(array_hashable):
sleep(1)
array = np.array(array_hashable)
return array * array
square(tuple(a))
Run Code Online (Sandbox Code Playgroud)
但我想把它全部包装在一个装饰器中,到目前为止我已经尝试过:
def np_cache(function):
def outter(array):
array_hashable = tuple(array)
@lru_cache()
def inner(array_hashable_inner):
array_inner = np.array(array_hashable_inner)
return function(array_inner)
return inner(array_hashable)
return outter
@np_cache
def square(array):
sleep(1)
return array * array
Run Code Online (Sandbox Code Playgroud)
但是缓存不起作用。计算已执行但未正确缓存,因为我总是等待 1 秒。
我在这里缺少什么?我猜lru_cache没有得到正确的上下文并且在每次调用中都被实例化,但我不知道如何解决它。
我试过盲目地functools.wraps到处乱扔装饰器,但没有运气。
Mar*_*ers 16
每次调用时,您的包装函数都会创建一个新inner()函数。那个新的函数对象在那个时候被装饰,所以最终的结果是每次outter()调用时,lru_cache()都会创建一个新的,并且它会是空的。空缓存将始终必须重新计算该值。
您需要创建一个装饰器,将缓存附加到每个装饰目标仅创建一次的函数。如果要在调用缓存之前转换为元组,则必须创建两个函数:
from functools import lru_cache, wraps
def np_cache(function):
@lru_cache()
def cached_wrapper(hashable_array):
array = np.array(hashable_array)
return function(array)
@wraps(function)
def wrapper(array):
return cached_wrapper(tuple(array))
# copy lru_cache attributes over too
wrapper.cache_info = cached_wrapper.cache_info
wrapper.cache_clear = cached_wrapper.cache_clear
return wrapper
Run Code Online (Sandbox Code Playgroud)
该cached_wrapper()函数在每次调用时只创建一次,np_cache()并且wrapper()作为闭包可供该函数使用。所以wrapper()调用cached_wrapper(),它有一个@lru_cache()附加的,缓存你的元组。
我还复制了lru_cache放置装饰函数的两个函数引用,因此它们也可以通过返回的包装器访问。
此外,我还使用@functools.wraps()装饰器将元数据从原始函数对象复制到包装器,例如名称、注释和文档字符串。这始终是一个好主意,因为这意味着您的装饰函数将在回溯、调试时以及您需要访问文档或注释时清楚地标识。装饰器还添加了一个__wrapped__指向原始函数的属性,如果需要,它可以让您再次解开装饰器。