★Python 集合(set)实战指南:告别低效循环,写出高性能优雅代码,面向 Python 开发者,从基础用法到性能对比、实战场景、踩坑总结,全部代码可直接复制运行,帮你彻底吃透 set。
一、开篇:从一段低效代码说起
你是否还在用多层循环做去重、查重复、比对列表? 先看一段非常典型的"新手写法":
# 低效查找重复元素:O(n²) 复杂度some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']duplicates = []for value in some_list:if some_list.count(value) > 1and value notin duplicates: duplicates.append(value)print(duplicates) # ['b', 'n']
问题很明显:
- 性能差:
count + in 双重遍历,O(n²) 复杂度
用 Counter + 集合推导式,O(n) 实现:
from collections import Countersome_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']counts = Counter(some_list) # 一次遍历统计所有频次duplicates = {x for x, c in counts.items() if c > 1}print(duplicates) # {'b', 'n'}
★⚠️ 常见误区:{x for x in some_list if some_list.count(x) > 1} 看起来是"一行",但内部每次仍调用 count 遍历全表,复杂度依然是 O(n²),并没有解决性能问题。真正的优化是先用 Counter 一次性统计频次,再做过滤。
二、集合基础:核心特性与创建
set 是 Python 内置无序、不重复、可变的哈希集合,是处理唯一性、成员判断的最优结构。
2.1 四种创建方式
# 1. 直接创建s1 = {1, 2, 3}# 2. 从可迭代对象创建(自带去重)s2 = set([1, 2, 2, 3, 3])s3 = set('pythonnn')# 3. 空集合(重要坑点)empty_set = set() # 正确empty_dict = {} # 这是字典!# 4. 集合推导式s4 = {x for x in range(10) if x % 2 == 0}
2.2 核心特性
- 元素必须可哈希(int/str/tuple 等不可变类型)
三、高频实战场景(开发必备)
场景 1:列表快速去重
lst = [1, 3, 2, 3, 1, 4]unique_lst = list(set(lst))print(unique_lst)
有序去重(Python 3.7+):
lst = ['a', 'b', 'c', 'b', 'd', 'a']ordered_unique = list(dict.fromkeys(lst))print(ordered_unique) # ['a', 'b', 'c', 'd']
场景 2:海量数据存在性判断(性能碾压 list)
import timebig_list = list(range(1_000_000))big_set = set(big_list)target = 999999# list 查找 O(n)t1 = time.time()target in big_listprint('list 耗时:', time.time() - t1)# set 查找 O(1)t2 = time.time()target in big_setprint('set 耗时:', time.time() - t2)
★数据量越大,set 优势越恐怖,适合用户 ID、权限、黑名单校验。
场景 3:交集(共同元素)
valid_colors = {'red', 'blue', 'green', 'black'}user_input = {'red', 'brown', 'blue'}valid = valid_colors & user_input# valid = valid_colors.intersection(user_input)print(valid) # {'red', 'blue'}
场景 4:差集(找出差异/非法内容)
invalid = user_input - valid_colorsprint(invalid) # {'brown'}
场景 5:并集(合并去重)
tag1 = {'Python', '后端', '开发'}tag2 = {'Python', '数据分析', '可视化'}all_tags = tag1 | tag2print(all_tags)
场景 6:对称差集(只在一边出现的元素)
a = {1, 2, 3}b = {3, 4, 5}print(a ^ b) # {1, 2, 4, 5}
四、集合常用方法速查表
| | |
|---|
add(x) | | |
remove(x) | | |
discard(x) | | |
pop() | | |
clear() | | |
intersection | & | |
union | | | |
difference | - | |
symmetric_difference | ^ | |
issubset | <= | |
issuperset | >= | |
五、高频踩坑与解决方案
坑 1:不能存 list / dict 等可变类型
# 报错# s = {[1,2], [3,4]}# 正确:转 tuples = {(1, 2), (3, 4)}
坑 2:{} 不是空集合,是空字典
print(type({})) # <class 'dict'>print(type(set())) # <class 'set'>
坑 3:set 无序,不要依赖顺序写逻辑
需要有序且唯一 → 使用 dict.fromkeys 或 list + set 组合。
六、进阶:不可变集合 frozenset
不可变、可哈希,可做字典 key 或集合元素。
fs = frozenset([1, 2, 3])d = {fs: "可作为key"}s = {fs, frozenset([4, 5])}
适用场景:配置常量、缓存 key、嵌套集合。
七、总结:什么时候优先用 set?
一句话:凡是和"唯一性、存在性、集合运算"相关的逻辑,优先想到 set,性能与代码简洁度直接上一个档次。