gpt4 book ai didi

python - 对 `lru_cache` 装饰函数使用可变参数可能会产生什么困难?

转载 作者:太空宇宙 更新时间:2023-11-04 00:34:39 24 4
gpt4 key购买 nike

在评论中:Is there a decorator to simply cache function return values?

@gerrit 指出了将可变但可散列的对象用于带有 functools.lru_cache 装饰器的函数的问题:

If I pass a hashable, mutable argument, and change the value of the object after the first call of the function, the second call will return the changed, not the original, object. That is almost certainly not what the user wants.

根据我的理解,假设可变对象的 __hash__() 函数是手动定义的以散列成员变量(而不只是使用对象的 id () 是自定义对象的默认值),更改参数对象将更改哈希,因此,对 lru_cache 装饰函数的第二次调用不应使用缓存。

如果为可变参数正确定义了 __hash__() 函数,是否存在因对 lru_cache 装饰函数使用可变参数而引起的任何无人值守行为?

最佳答案

我的评论是错误的/误导性的,与 lru_cache 无关,但与任何创建更通用的缓存函数的尝试有关。

我需要一个缓存函数,该函数适用于输入和输出 NumPy 数组的函数,这些数组是可变的且不可散列的。因为 NumPy 数组不可散列,所以我无法使用 functools.lru_cache。我最终写了这样的东西:

def mutable_cache(maxsize=10):
"""In-memory cache like functools.lru_cache but for any object

This is a re-implementation of functools.lru_cache. Unlike
functools.lru_cache, it works for any objects, mutable or not.
Therefore, it returns a copy and it is wrong if the mutable
object has changed! Use with caution!

If you call the *resulting* function with a keyword argument
'CLEAR_CACHE', the cache will be cleared. Otherwise, cache is rotated
when more than `maxsize` elements exist in the cache. Additionally,
if you call the resulting function with NO_CACHE=True, it doesn't
cache at all. Be careful with functions returning large objects.
Everything is kept in RAM!

Args:
maxsize (int): Maximum number of return values to be remembered.

Returns:
New function that has caching implemented.
"""

sentinel = object()
make_key = functools._make_key

def decorating_function(user_function):
cache = {}
cache_get = cache.get
keylist = [] # don't make it too long

def wrapper(*args, **kwds):
if kwds.get("CLEAR_CACHE"):
del kwds["CLEAR_CACHE"]
cache.clear()
keylist.clear()
if kwds.get("NO_CACHE"):
del kwds["NO_CACHE"]
return user_function(*args, **kwds)
elif "NO_CACHE" in kwds:
del kwds["NO_CACHE"]
key = str(args) + str(kwds)
result = cache_get(key, sentinel)
if result is not sentinel:
# make sure we return a copy of the result; when a = f();
# b = f(), users should reasonably expect that a is not b.
return copy.copy(result)
result = user_function(*args, **kwds)
cache[key] = result
keylist.append(key)
if len(keylist) > maxsize:
try:
del cache[keylist[0]]
del keylist[0]
except KeyError:
pass
return result

return functools.update_wrapper(wrapper, user_function)

return decorating_function

在我的第一个版本中,我省略了 copy.copy() 函数(实际上应该是 copy.deepcopy()),如果我这样做会导致错误更改了结果值,然后调用缓存函数。添加 copy.copy() 功能后,我意识到在某些情况下我占用了内存,主要是因为我的函数计算的是对象,而不是总内存使用量,这在一般情况下并不容易在 Python 中(尽管如果限于 NumPy 数组应该很容易)。因此,我将 NO_CACHECLEAR_CACHE 关键字添加到生成的函数中,它们按照其名称所建议的方式执行操作。

在编写和使用这个函数之后,我明白了 functools.lru_cache 只适用于具有可散列输入参数的函数的原因不止一个。任何需要使用可变参数的缓存函数的人都需要非常小心。

关于python - 对 `lru_cache` 装饰函数使用可变参数可能会产生什么困难?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44583381/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com