直接用 set() 去重会丢失顺序,因为 set 是无序哈希表;需保持首次出现顺序时应使用 dict.fromkeys(),它在 python 3.7+ 中保留插入顺序。

为什么直接用 set() 去重会丢失顺序?
因为 set 本质是无序哈希表,构造时元素位置不保留。比如 list(set([1,2,2,3,1])) 可能返回 [2, 1, 3],顺序完全不可控。
如果需要去重且保持首次出现顺序,别用 set() 直接转回列表。推荐用 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 流程。
用 set 做交集、并集、差集的正确写法
Python 的 set 支持运算符和方法两种语法,语义一致但行为细节不同:
-
&和set.intersection()都求交集,但后者支持多个参数:set_a.intersection(set_b, set_c);而&只能两两链式:set_a & set_b & set_c -
|和set.union()同理,后者更灵活 -
-是左减右(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 要求元素可哈希(hashable),所以包含列表、字典、集合本身的嵌套结构会直接报错:
bad = [[1,2], [3,4]] set(bad) # TypeError: unhashable type: 'list'
常见踩坑场景:
- 想对含字典的列表去重 → 先转成
tuple或frozenset(如字典可排序后json.dumps(sorted_items)再哈希) - 二维数字列表去重 → 用
tuple(row)转每行:set(map(tuple, matrix)) - 字符串列表没问题,但注意大小写:
set(['A', 'a'])是两个元素
没报错不等于逻辑正确——检查原始数据是否真能被哈希,比看语法是否通过更重要。
性能差异:什么时候该用 set,什么时候不该?
小列表(set 反而可能更慢,因为构造 set 有初始化开销;大列表(>1000)优势明显,尤其做多次成员判断(in 操作)。
典型高性价比场景:
- 判断一个元素是否在大量数据中存在 →
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。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











