问题描述
我正在尝试为具有numpy数组输入参数的函数创建缓存装饰器
I am trying to make a cache decorator for functions with numpy array input parameters
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数组不可散列,
But numpy arrays are not hashable,
TypeError Traceback (most recent call last)
<ipython-input-13-559f69d0dec3> in <module>()
----> 1 square(a)
TypeError: unhashable type: 'numpy.ndarray'
因此需要将它们转换为元组.我可以正常工作并进行缓存:
So they need to be converted to tuples. I have this working and caching correctly:
@lru_cache()
def square(array_hashable):
sleep(1)
array = np.array(array_hashable)
return array * array
square(tuple(a))
但是我想将它们全部包装在一个装饰器中,到目前为止,我已经尝试过:
But I wanted to wrap it all up in a decorator, so far I have tried:
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秒钟.
But caching is not working. Computation is performed but not cached properly, as I am always waiting 1 second.
我在这里想念什么?我猜lru_cache
不能正确使用上下文,并且在每个调用中都将其实例化,但是我不知道如何解决它.
What am I missing here? I'm guessing lru_cache
isn't getting the context right and its being instantiated in each call, but I don't know how to fix it.
我试图四处乱扔functools.wraps
装饰器,但是没有运气.
I have tried blindly throwing the functools.wraps
decorator here and there with no luck.
推荐答案
您的包装函数每次调用都会创建一个新的inner()
函数.并且那个新的函数对象在那个时候被修饰,所以最终结果是每次调用outter()
时,都会创建一个新的lru_cache()
,并且它将为空.空缓存将始终需要重新计算该值.
Your wrapper function creates a new inner()
function each time you call it. And that new function object is decorated at that time, so the end result is that each time outter()
is called, a new lru_cache()
is created and that'll be empty. An empty cache will always have to re-calculate the value.
您需要创建一个装饰器,将装饰器附加到每个装饰目标仅一次 创建的函数.如果要在调用缓存之前转换为元组,则必须创建两个函数:
You need to create a decorator that attaches the cache to a function created just once per decorated target. If you are going to convert to a tuple before calling the cache, then you'll have to create two functions:
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()
)来缓存您的元组.
The cached_wrapper()
function is created just once per call to np_cache()
and is available to the wrapper()
function as a closure. So wrapper()
calls cached_wrapper()
, which has a @lru_cache()
attached to it, caching your tuples.
我还复制了lru_cache
放在装饰函数上的两个函数引用,因此也可以通过返回的包装器对其进行访问.
I also copied across the two function references that lru_cache
puts on a decorated function, so they are accessible via the returned wrapper as well.
此外,我还使用了 @functools.wraps()
装饰器可以将元数据从原始函数对象复制到包装器,例如名称,注释和文档字符串.这始终是一个好主意,因为这意味着在调试以及何时需要访问文档或注释时,将在回溯中清楚地标识修饰后的功能.装饰器还添加了指向原始函数的__wrapped__
属性,该属性将如果需要的话,让您再次打开装饰器.
In addition, I also used the @functools.wraps()
decorator to copy across metadata from the original function object to the wrapper, such as the name, annotations and documentation string. This is always a good idea, because that means your decorated function will be clearly identified in tracebacks, when debugging and when you need to access documentation or annotations. The decorator also adds a __wrapped__
attribute pointing back to the original function, which would let you unwrap the decorator again if need be.
这篇关于numpy数组的缓存装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!