咨询:使用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




