You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

咨询:使用functools.lru_cache缓存带列表参数的函数报错问题

解决functools.lru_cache因列表参数触发TypeError: unhashable type: 'list'的问题

这个错误的核心原因很直白:lru_cache是靠参数的哈希值生成缓存键的,而列表是可变、不可哈希的类型(它的内容可以随时修改,哈希值会跟着变化),所以直接把列表当参数传入就会触发报错。下面给你几个实用的解决思路:

方法1:手动将列表转成元组(最直接)

元组是不可变、可哈希的,完美适配lru_cache的要求。你可以选择在调用时转换,或者用包装函数自动处理:

方式A:调用时手动转元组

from functools import lru_cache

@lru_cache(maxsize=None)
def calculate_sum(num_tuple):
    # 如果函数逻辑需要列表,再把元组转回列表即可
    num_list = list(num_tuple)
    return sum(num_list)

# 调用时把列表转成元组传入
calculate_sum(tuple([1, 2, 3]))
calculate_sum(tuple([1, 2, 3]))  # 第二次调用会直接命中缓存

方式B:用包装器自动处理列表转元组

不想每次调用都手动转?写个简单的装饰器包装一下,让函数支持直接传列表:

from functools import lru_cache, wraps

def cache_list_args(func):
    @wraps(func)
    def wrapper(input_list):
        # 内部自动把列表转成元组传给原函数
        return func(tuple(input_list))
    # 给包装后的函数加上缓存能力
    return lru_cache(maxsize=None)(wrapper)

@cache_list_args
def calculate_sum(num_tuple):
    return sum(num_tuple)

# 直接传列表就行,无需手动转换
calculate_sum([1, 2, 3])
calculate_sum([1, 2, 3])  # 命中缓存

方法2:用支持自定义键函数的缓存工具(更灵活)

如果你的函数有多个参数,或者参数里混合了列表和其他类型,可以用cachetools库(需要先pip install cachetools),它允许你自定义生成缓存键的逻辑:

from cachetools import cached, LRUCache
from cachetools.keys import hashkey

def make_hashable_key(*args, **kwargs):
    # 处理所有位置参数:把列表转成元组,其他类型保持原样
    processed_args = tuple(
        tuple(arg) if isinstance(arg, list) else arg
        for arg in args
    )
    # 处理所有关键字参数:同样把列表转成元组
    processed_kwargs = {
        key: tuple(val) if isinstance(val, list) else val
        for key, val in kwargs.items()
    }
    # 用处理后的参数生成合法的缓存键
    return hashkey(*processed_args, **processed_kwargs)

@cached(cache=LRUCache(maxsize=None), key=make_hashable_key)
def complex_calculation(num_list, multiplier=2):
    return sum(num_list) * multiplier

# 直接传列表参数,缓存正常工作
complex_calculation([1, 2, 3], multiplier=2)
complex_calculation([1, 2, 3])  # 命中缓存

注意事项

如果你的列表里嵌套了其他不可哈希的类型(比如另一个列表、字典),那转成元组也没用——这时候你需要把所有嵌套的可变类型都转成可哈希的(比如嵌套列表转嵌套元组),或者考虑将参数序列化为字符串当缓存键,不过这种场景日常开发里比较少见。

内容的提问来源于stack exchange,提问作者redfast00

火山引擎 最新活动