如何用 Python 的 set 集合做去重、交并集与性能判断

Noah
Noah
发布时间:2026-08-25| 148人浏览过
原创

前言

在 Python 里,`set` 常被拿来做列表去重和交并集运算,但很多问题恰恰出在“会用”却没分清边界:它去重快,却不保序;运算简洁,却有类型和场景限制。本文把去重顺序、集合运算写法、可哈希约束和性能判断放到同一条使用链路里,帮你更快选对写法。

很多人第一次用 Python 做列表去重,都会顺手写出 list(set(data))。它确实快,但一旦业务要求“去重后保留原始顺序”,这个写法就会立刻出问题。下面就按实际使用场景拆开讲:先看 set 为什么会打乱顺序,再看交并差集怎么写更顺手,最后补上类型限制和性能边界,方便你判断什么时候该用、什么时候该换方案。

为什么 set() 去重会打乱原有顺序

set 的本质是无序哈希表,元素放进去以后,不会按你原来列表里的位置保存。因此,直接把列表丢进 set() 再转回列表,得到的只是“去重后的某种排列”,而不是“按原顺序去重后的结果”。

列表去重时 set 与 dict.fromkeys 的顺序差异示意图
去重结果为什么会变序用纸张卡片对比“无序去重”和“保留首次顺序”两种结果,帮助读者快速判断该选哪种写法。

例如下面这段代码:

list(set([1,2,2,3,1]))

它可能返回 [2, 1, 3],也可能表现出别的顺序。重点不在于某次运行恰好是什么,而在于这个顺序本身不可控,不能拿来依赖。

如果需求是“保留第一次出现的顺序再去重”,更合适的做法是用 dict.fromkeys()。在 Python 3.7+ 中,字典会保留插入顺序,所以它正好适合这种场景:

original = [1, 2, 2, 3, 1, 4]
unique_ordered = list(dict.fromkeys(original))# [1, 2, 3, 4]

这里要分清楚:不是 set 不好用,而是它解决的是“无序去重”问题。如果你的业务要求包含顺序信息,那就不应该把纯 set 当成最终方案。

交集、并集、差集和对称差应该怎么写

除了去重,set 更常见的价值在集合运算。Python 同时提供了运算符写法和方法写法,两套语义基本一致,但适用场景略有区别。

Python set 交并差集与对称差写法对照图
四种集合运算怎么选写法把运算符写法和方法写法放在同一张编辑卡片里,便于区分简洁写法与多参数写法。

交集:找共同元素

交集可以用 &,也可以用 intersection():

  • set_a & set_b
  • set_a.intersection(set_b)

如果只处理两个集合,运算符最直观;如果要一次处理多个集合,方法更灵活,例如:set_a.intersection(set_b, set_c)。而运算符写法只能链式写成 set_a & set_b & set_c。

并集:合并并自动去重

并集对应 | 或 union()。两者思路相同,都是把多个集合合在一起并自动去重。处理固定数量集合时,| 更短;集合数量不固定、需要动态传参时,union() 会更顺手。

列表转 set 前的类型限制与性能判断示意图
先看类型,再看性能收益把“能不能转”和“值不值得转”放进同一张专栏图,帮助读者减少语法通过但逻辑出错的情况。

差集:只保留左边独有的元素

set_a - set_b 表示“左边有、右边没有”的元素,等价于 set_a.difference(set_b)。不过方法写法还有一个优势:可以一次减多个集合,例如 set_a.difference(set_b, set_c),而 - 只能写成链式运算。

对称差:保留不重叠的部分

^ 表示对称差,也就是“属于 a 或 b,但不同时属于两边”的元素,等价于 set_a.symmetric_difference(set_b)。这个运算在比较两份数据差异时很实用,尤其适合快速找出“不一致项”。

实际开发里,可以把规则记成一句话:日常写法优先用 &、|、-、^,代码更紧凑;一旦涉及多个集合或参数来自变量列表,再换成对应的方法。

列表转 set 前,先确认元素是否可哈希

set 不是任何数据都能直接放。它要求元素必须是可哈希的,也就是能稳定参与哈希计算。像列表、字典、集合这类可变对象,默认都不能作为 set 元素。

下面这个例子会直接报错:

bad = [[1,2], [3,4]]
set(bad)# TypeError: unhashable type: 'list'

这类问题在数据清洗时很常见,尤其是列表里套列表、套字典的时候。几个高频场景可以这样处理:

  • 二维数字列表去重:先把每一行转成元组,再做集合化,例如 set(map(tuple, matrix))
  • 想对含字典的列表去重:先把字典转换成可哈希形式,例如转成 tuple、frozenset,或者在字段顺序可控时使用 json.dumps(sorted_items) 后再参与哈希
  • 字符串列表通常可以直接转 set,但大小写会被视为不同值,比如 set(['A', 'a']) 会得到两个元素

这里最容易忽略的一点是:代码没报错,只代表类型通过了,不代表业务逻辑一定正确。尤其是做去重时,先确认“两个对象在你的业务里算不算同一个值”,往往比语法本身更关键。

性能判断:什么时候该用 set,什么时候别硬上

set 的性能优势主要体现在成员判断和大规模集合运算上,但不是所有场景都值得先转一次集合。

如果数据量很小,比如少于 100 个元素,构造 set 本身的初始化开销可能并不划算;而当数据量上升到 1000 以上,尤其需要频繁做 in 判断时,它的优势会明显放大。

适合用 set 的场景

  • 大量成员判断:if x in big_set: 的平均复杂度是 O(1),通常比 if x in big_list: 的 O(n) 更高效
  • 批量过滤:先把黑名单放进 set,再过滤数据,例如 filter(lambda x: x not in black_set, data)
  • 多个来源 ID 合并去重:把各来源列表转成集合后再做 | 运算,通常会比嵌套循环快很多

不一定要用 set 的场景

如果你只是做一次性去重,后面还要保留原顺序,甚至继续按索引访问,那么 dict.fromkeys() 往往比“先转 set、再排序、再转回列表”更直接。判断标准很简单:别为了用 set 而用 set,而是看它是否真的匹配当前的数据结构和后续操作。

一条实用判断线:先看需求,再选结构

把这篇内容压缩成可执行的判断,其实只有几条:

  • 只想快速去重,不关心顺序,用 set()
  • 既要去重又要保留首次出现顺序,用 dict.fromkeys(),适用 Python 3.7+
  • 做交集、并集、差集、对称差,优先用运算符;多集合或动态参数时再换方法
  • 列表转集合前,先检查元素是否可哈希,尤其注意嵌套列表和字典
  • 性能收益主要出现在大数据量和高频查找上,小数据场景别过度设计

这样处理后,set 就不只是“一个能去重的容器”,而是一个在合适场景下非常高效的数据结构工具。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。