Python集合去重原理是什么_set高效去重技巧【教程】

Python集合去重靠哈希表实现,平均时间复杂度O(1)每次插入,整体O(n);依赖对象可哈希性,不可变类型如int/str/tuple可入set,list/dict/set则不可;保序去重推荐dict.fromkeys(),浮点精度、自定义类未重写__hash__和__eq__、字符串未归一化是三大易错点。

Python 集合去重靠的是哈希表(hash table)底层实现,不是遍历比对。这意味着 set 去重平均时间复杂度是 O(1) 每次插入,整体 O(n),远快于用 list 手动查重的 O(n²)。

为什么 set 能秒级去重?

因为 set 内部用哈希表存储元素:每个元素先算 hash(),映射到固定桶位;冲突时用开放寻址或链地址法处理。只要对象可哈希(immutable 且实现 __hash____eq__),就能进 set

  • 可哈希类型:intstrtuple(不含可变项)、frozenset
  • 不可哈希类型:listdictset —— 直接加会报 TypeError: unhashable type
  • hash() 值相同且 __eq__ 返回 True 的对象,会被视为重复项

常见去重场景与写法对比

别无脑转 list(set(...)) —— 它不保序,还可能出错。

  • 保序去重(首次出现优先):
    dict.fromkeys(items) 最快最安全,Python 3.7+ 保证插入顺序
    等价但更直观:用 dict 当“有序集合”容器
  • 含嵌套结构(如 list)要去重:
    先转成可哈希形式,例如 tuple(item)(仅当 item 是一维 list 且元素可哈希)
    或用 frozenset 处理无序集合型数据
  • 大数据量时避免一次性构造:
    用生成器 + set 边读边判重:
    seen = set()
    unique_items = []
    for item in large_iterable:
        if item not in seen:
            seen.add(item)
            unique_items.append(item)

set 去重的三个易踩坑点

这些错误不会报错,但结果不对,调试起来很隐蔽。

  • 浮点数精度导致哈希不一致:0.1 + 0.2 != 0.3,进 set 可能被当成不同元素
  • 自定义类没重写 __hash____eq__:默认按内存地址哈希,两个值相同的实例仍算不同
  • 字符串首尾空格或大小写未归一化:"abc ""abc" 是两个不同 strset 不会自动 strip 或 lower

真正难的不是“怎么去重”,而是想清楚“什么才算重复”——哈希只认字面值相等,不理解业务语义。比如日期字符串 "2025-01-01"datetime.date(2025, 1, 1),在 set 里永远不重复,哪怕你心里觉得它们代表同一天。