Python numpy阵列的缓存装饰器

Python numpy阵列的缓存装饰器,python,python-3.x,numpy,caching,decorator,Python,Python 3.x,Numpy,Caching,Decorator,我正在尝试为带有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) 但是numpy数组是不可散列的 TypeError

我正在尝试为带有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)
但是numpy数组是不可散列的

TypeError                                 Traceback (most recent call last)
<ipython-input-13-559f69d0dec3> in <module>()
----> 1 square(a)

TypeError: unhashable type: 'numpy.ndarray'
但我想用一个装饰师来包装这一切,到目前为止我已经尝试过:

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
但是,缓存不起作用。计算已执行,但未正确缓存,因为我总是等待1秒

我错过了什么?我猜
lru\u cache
没有获得正确的上下文并在每次调用中实例化它,但我不知道如何修复它


我曾试图盲目地抛出
functools.wrapps
装饰程序,但运气不好。

您的包装函数每次调用时都会创建一个新的
内部()
函数。此时新函数对象被修饰,因此最终结果是每次调用
outter()
,都会创建一个新的
lru\u缓存()
,该缓存将为空。空缓存将始终必须重新计算该值

您需要创建一个装饰器,将缓存附加到每个装饰目标只创建一次的函数。如果要在调用缓存之前转换为元组,则必须创建两个函数:

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
cached\u wrapper()
函数在每次调用
np\u cache()
时仅创建一次,并作为闭包提供给
wrapper()
函数。所以
wrapper()
调用
cached\u wrapper()
,它附带了一个
@lru\u cache()
,缓存元组

我还复制了
lru\u cache
放在修饰函数上的两个函数引用,因此它们也可以通过返回的包装器访问


此外,我还使用跨元数据从原始函数对象复制到包装器,例如名称、注释和文档字符串。这始终是一个好主意,因为这意味着在调试时以及需要访问文档或注释时,您的修饰函数将在回溯中清楚地标识出来。装饰程序还添加了一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>属性,指向原始函数,这将是。

我想补充的是,通过将数组赋给np\u cache()中定义的非局部变量,您可以在wrapper()和cashed\u wrapper()之间共享数组,这样,你不能。它不是线程安全的。您无法确定传递给
cached\u wrapper()
的是同一数组的元组值版本。这两个函数之间共享的任何闭包都应该被视为每个被修饰的
函数范围内的全局闭包。
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