python - numpy 数组的缓存装饰器

标签 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                                 Traceback (most recent call last)
<ipython-input-13-559f69d0dec3> in <module>()
----> 1 square(a)

TypeError: unhashable type: 'numpy.ndarray'

因此需要将它们转换为元组。我有这个工作和缓存正确:

@lru_cache()
def square(array_hashable):
    sleep(1)
    array = np.array(array_hashable)
    return array * array

square(tuple(a))

但我想把它全部包装在一个装饰器中,到目前为止我已经尝试过:

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_cache 没有获得正确的上下文并且它在每次调用中都被实例化,但我不知道如何修复它。

我试过盲目地到处乱扔 functools.wraps 装饰器,但没有成功。

最佳答案

每次调用时,您的包装函数都会创建一个新的 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

cached_wrapper() 函数仅在每次调用 np_cache() 时创建一次,并且可作为 wrapper() 函数使用关闭。所以 wrapper() 调用 cached_wrapper(),它有一个 @lru_cache() 附加到它,缓存你的元组。

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

此外,我还使用了@functools.wraps() decorator将元数据从原始函数对象复制到包装器,例如名称、注释和文档字符串。这总是一个好主意,因为这意味着你的修饰函数将在回溯中、调试时以及你需要访问文档或注释时被清楚地识别出来。装饰器还添加了一个指向原始函数的 __wrapped__ 属性,这将是 let you unwrap the decorator again if need be .

关于python - numpy 数组的缓存装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52331944/

相关文章:

python - Hook Python 标准库模块

python - 生成 numpy.MultivariateNormal 时出现 MemoryError

python - 哪些 HMM(隐马尔可夫模型)库对 Python 有很好的支持?

Python:从项目层次结构中同一级别的另一个目录导入模块

python - 使用 numpy 运算从每行填充 numpy 数组(不包括填充)和非填充值的数量中选择随机数的最快方法

c# - 在 Emgu 中使用 Erode 和 Dilate (OpenCV for C#)

python - 如何将输入读取为数字?

python - 使用 TensorFlow 作为后端的 keras 出现错误

numpy - 使用 numpy 计算二进制掩码 IOU 的最快方法

python - H5Py 和存储