用 set() 去重会丢失原始顺序;需保序时应使用 dict.fromkeys(seq),它利用字典键唯一性和插入顺序保证,如 list(dict.fromkeys([1,2,2,3,1])) → [1, 2, 3]。

直接用 set() 去重会丢顺序,别无脑套
Python 列表转 set 确实能去重,但 set 本身无序,原列表顺序一概清零。如果你只是统计唯一值、不关心出现先后,那没问题;但多数实际场景(比如 dedupe 用户操作日志、保留首次出现的 URL)都要求“去重但保序”。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 要保序:改用
dict.fromkeys(seq),它利用字典键的唯一性和 Python 3.7+ 的插入顺序保证,list(dict.fromkeys([1,2,2,3,1]))→[1, 2, 3] - 兼容老版本(seen = set() 辅助判断,边遍历边收集未见过的元素
- 别对含不可哈希元素(如
dict、list)的序列直接调set(),会报TypeError: unhashable type
intersection()、union()、difference() 这些方法比符号运算符慢一点,但可读性更强
集合运算有两套写法:a & b 和 a.intersection(b) 效果一样,但行为细节不同。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 符号运算(
&、|、-)只支持同为set的操作数;而.intersection()等方法可以接受任意可迭代对象,比如my_set.intersection([1,2,2,3])合法,my_set & [1,2,2,3]直接报TypeError - 多个集合运算时,
a & b & c比a.intersection(b, c)略快,但差距微乎其微,优先选可读性——尤其当参数是变量名而非字面量时,allowed_types.intersection(user_input)比allowed_types & user_input更易懂 -
difference()是左结合,a - b - c等价于(a - b) - c,不是a - (b | c);如果本意是排除多个集合,明确写成a.difference(b, c)或a - (b | c)
嵌套结构里不能直接用 set,得先转成可哈希形式
常见错误现象:set([[1,2], [3,4]]) 报 TypeError: unhashable type: 'list'。因为 list、dict、set 本身不可哈希,不能作为 set 元素或字典键。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 若元素是固定长度小列表(如坐标
[x,y]),转成tuple:set(map(tuple, list_of_lists)) - 若含深层嵌套或类型混杂,考虑用
json.dumps(obj, sort_keys=True)生成字符串再塞进set,但注意浮点精度、NaN 处理等副作用 - 别用
frozenset做“万能解”,它虽可哈希,但语义上表示“不可变集合”,和原始数据意图可能错位;且frozenset([1,2]) == frozenset([2,1]),顺序敏感场景会误判
空集合初始化必须用 set(),不能写 {}
这是新手高频踩坑点:{} 是空字典,不是空集合。写 my_set = {} 再调 my_set.add(1) 会报 AttributeError: 'dict' object has no attribute 'add'。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 初始化空集合,只认
set();带初始值可用{1, 2, 3}字面量 - 函数参数默认值慎用可变对象,
def f(items=None): items = items or set()比def f(items=set())安全得多 - 检查变量是否为集合,用
isinstance(x, set),别靠type(x) is set—— 子类化set的自定义类也会被合理识别
集合运算看着简单,真正卡住人的往往不是语法,而是“这个数据到底能不能哈希”“顺序到底要不要保”“空集合我是不是又写成字典了”——这些点不跑一遍真实数据,光看文档很难意识到。










