集合元素必须可哈希,即只能是不可变类型(如int、str、tuple、frozenset等),因set底层依赖hash()实现O(1)查找与去重;list、dict、set等可变类型因哈希值可能变化而被禁止。

集合元素必须可哈希,而可变类型不可哈希
Python 的 set 底层用哈希表实现,依赖每个元素的 hash() 值快速定位、去重和查找。可变对象(如 list、dict、set)的值可能在创建后被修改,一旦内容变了,hash() 值也会变——但哈希表里原来存的位置不会自动更新,这会导致查不到、重复、甚至崩溃。
所以 Python 强制要求:只有不可变类型(即“可哈希类型”)才能当集合元素。
-
int、str、float、bool都行 -
tuple只要内部不含可变项(比如(1, "a")✅,但(1, [2])❌) -
frozenset可以,但普通set不行
尝试添加列表会直接报 TypeError: unhashable type: 'list'
这不是警告,是运行时立刻抛出的异常。哪怕只是写在字面量里,Python 解析器就会拒绝:
my_set = {1, 2, [3, 4]}执行这行就崩,错误信息非常明确:unhashable type: 'list'。不是语法错,也不是逻辑错,是类型系统层面的硬性限制。
立即学习“Python免费学习笔记(深入)”;
常见误操作包括:
- 想用
{[1, 2], [3, 4]}表示“一组列表”,结果报错 - 从其他结构(如 JSON 或数据库)读出 list 后直接塞进 set,没做转换
- 误以为
set([1, 2, 3])是把 list 当元素——其实那是把 list 当可迭代对象来构造 set,等价于{1, 2, 3}
元组能用,但嵌套可变项仍会失败
tuple 是不可变容器,所以常被当作“列表的只读替代品”用于集合。但要注意:元组的可哈希性取决于其内容是否全部可哈希。
这些可以:
{(1, 2), ("a", "b"), (1, "x", True)}这个不行:
{(1, [2, 3])} # TypeError: unhashable type: 'list'因为 [2, 3] 在元组内部,整个元组就变成不可哈希了。这种嵌套陷阱容易被忽略,尤其在处理嵌套数据结构(如 API 返回的 list of dicts)时。
需要存可变结构?考虑 frozenset 或序列转元组
如果确实要让一组可变对象(比如多个列表)去重或做集合运算,得先“冻结”它们:
- 把
list转成tuple:set(tuple(x) for x in list_of_lists) - 把
list或set转成frozenset:{frozenset([1,2]), frozenset([3,4])} - 对字典,可用
frozenset(d.items())冻结键值对
注意:转换本身有开销,且 tuple([1,2]) 和 frozenset([1,2]) 语义不同——前者有序、允许重复;后者无序、自动去重。选哪个取决于你要保留什么特征。
真正麻烦的地方不在语法,而在设计意图:你打算拿这个“集合”做什么?如果只是为了临时去重,转 tuple 最快;如果后续还要当字典 key,frozenset 更稳妥;如果原始结构复杂(比如含嵌套 dict),就得手动递归冻结,这时候往往该换数据结构了——比如用 json.dumps(sorted_items, sort_keys=True) 生成稳定字符串作 key。


















