在具有常量但不可散列对象的函数上使用 functools.lru_cache
Using functools.lru_cache on functions with constant but non-hashable objects
是否可以使用 functools.lru_cache
缓存由 functools.partial
创建的部分函数?
我的问题是一个函数接受可散列参数和连续的、不可散列的对象,例如 NumPy 数组。
考虑这个玩具示例:
import numpy as np
from functools import lru_cache, partial
def foo(key, array):
print('%s:' % key, array)
a = np.array([1,2,3])
由于 NumPy 数组不可散列,因此这行不通:
@lru_cache(maxsize=None)
def foo(key, array):
print('%s:' % key, array)
foo(1, a)
正如预期的那样,您会收到以下错误:
/Users/ch/miniconda/envs/sci34/lib/python3.4/functools.py in __init__(self, tup, hash)
349 def __init__(self, tup, hash=hash):
350 self[:] = tup
--> 351 self.hashvalue = hash(tup)
352
353 def __hash__(self):
TypeError: unhashable type: 'numpy.ndarray'
所以我的下一个想法是使用 functools.partial
摆脱 NumPy 数组(无论如何它是常量)
pfoo = partial(foo, array=a)
pfoo(2)
所以现在我有了一个只接受可散列参数的函数,应该非常适合 lru_cache
。但是在这种情况下可以使用 lru_cache
吗?我不能将它用作包装函数而不是 @lru_cache
装饰器,可以吗?
有什么巧妙的方法可以解决这个问题吗?
由于数组是常量,您可以在实际的 lru 缓存函数周围使用包装器,并简单地将键值传递给它:
from functools import lru_cache, partial
import numpy as np
def lru_wrapper(array=None):
@lru_cache(maxsize=None)
def foo(key):
return '%s:' % key, array
return foo
arr = np.array([1, 2, 3])
func = lru_wrapper(array=arr)
for x in [0, 0, 1, 2, 2, 1, 2, 0]:
print (func(x))
print (func.cache_info())
输出:
('0:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
CacheInfo(hits=5, misses=3, maxsize=None, currsize=3)
这里有一个如何使用 lru_cache
和 functools.partial
的例子:
from functools import lru_cache, partial
import numpy as np
def foo(key, array):
return '%s:' % key, array
arr = np.array([1, 2, 3])
pfoo = partial(foo, array=arr)
func = lru_cache(maxsize=None)(pfoo)
for x in [0, 0, 1, 2, 2, 1, 2, 0]:
print(func(x))
print(func.cache_info())
输出:
('0:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
CacheInfo(hits=5, misses=3, maxsize=None, currsize=3)
这比更简洁,也使用OP要求的functools.partial
。
P.S.: 此解决方案改编自
是否可以使用 functools.lru_cache
缓存由 functools.partial
创建的部分函数?
我的问题是一个函数接受可散列参数和连续的、不可散列的对象,例如 NumPy 数组。
考虑这个玩具示例:
import numpy as np
from functools import lru_cache, partial
def foo(key, array):
print('%s:' % key, array)
a = np.array([1,2,3])
由于 NumPy 数组不可散列,因此这行不通:
@lru_cache(maxsize=None)
def foo(key, array):
print('%s:' % key, array)
foo(1, a)
正如预期的那样,您会收到以下错误:
/Users/ch/miniconda/envs/sci34/lib/python3.4/functools.py in __init__(self, tup, hash)
349 def __init__(self, tup, hash=hash):
350 self[:] = tup
--> 351 self.hashvalue = hash(tup)
352
353 def __hash__(self):
TypeError: unhashable type: 'numpy.ndarray'
所以我的下一个想法是使用 functools.partial
摆脱 NumPy 数组(无论如何它是常量)
pfoo = partial(foo, array=a)
pfoo(2)
所以现在我有了一个只接受可散列参数的函数,应该非常适合 lru_cache
。但是在这种情况下可以使用 lru_cache
吗?我不能将它用作包装函数而不是 @lru_cache
装饰器,可以吗?
有什么巧妙的方法可以解决这个问题吗?
由于数组是常量,您可以在实际的 lru 缓存函数周围使用包装器,并简单地将键值传递给它:
from functools import lru_cache, partial
import numpy as np
def lru_wrapper(array=None):
@lru_cache(maxsize=None)
def foo(key):
return '%s:' % key, array
return foo
arr = np.array([1, 2, 3])
func = lru_wrapper(array=arr)
for x in [0, 0, 1, 2, 2, 1, 2, 0]:
print (func(x))
print (func.cache_info())
输出:
('0:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
CacheInfo(hits=5, misses=3, maxsize=None, currsize=3)
这里有一个如何使用 lru_cache
和 functools.partial
的例子:
from functools import lru_cache, partial
import numpy as np
def foo(key, array):
return '%s:' % key, array
arr = np.array([1, 2, 3])
pfoo = partial(foo, array=arr)
func = lru_cache(maxsize=None)(pfoo)
for x in [0, 0, 1, 2, 2, 1, 2, 0]:
print(func(x))
print(func.cache_info())
输出:
('0:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('1:', array([1, 2, 3]))
('2:', array([1, 2, 3]))
('0:', array([1, 2, 3]))
CacheInfo(hits=5, misses=3, maxsize=None, currsize=3)
这比functools.partial
。
P.S.: 此解决方案改编自