Python中的set集合是最常用的去重工具,它的去重效率远高于手动遍历列表判断重复的方式,核心原因在于其底层基于哈希表实现。哈希表的结构让元素查找、插入的时间复杂度接近O(1),因此去重过程不需要逐次比对所有已有元素。

Python集合去重的核心原理
set的底层实现是哈希表,当我们向集合中添加一个元素时,会先执行以下步骤:
- 首先计算元素的哈希值,Python中通过内置的
hash()函数实现,哈希值是整数类型 - 根据哈希值确定元素在哈希表中的存储位置
- 如果该位置没有元素,直接插入;如果该位置已有元素,会判断两个元素是否相等(调用
__eq__方法) - 如果相等则判定为重复元素,不会插入;如果不相等则处理哈希冲突后插入
这里要注意,只有可哈希的对象才能放入set中,比如整数、字符串、元组都是可哈希的,而列表、字典这类不可哈希的对象不能作为set的元素,否则会抛出TypeError错误。
基础set去重技巧
1. 简单可哈希元素去重
对于整数、字符串这类基础类型的重复数据,直接用set()转换即可完成去重:
# 列表去重示例 origin_list = [1, 2, 3, 2, 4, 1, 5] deduplicated_set = set(origin_list) # 如果需要转回列表 result_list = list(deduplicated_set) print(result_list)
这种方式的缺点是set是无序的,转回列表后元素的原始顺序会丢失。
2. 去重同时保留原始顺序
如果需要在去重的同时保留元素第一次出现的顺序,可以用字典的fromkeys方法,因为Python3.7之后字典会保留插入顺序:
origin_list = [1, 2, 3, 2, 4, 1, 5] # 利用字典key的唯一性去重,同时保留顺序 result_list = list(dict.fromkeys(origin_list)) print(result_list)
复杂场景去重技巧
1. 元组或自定义对象去重
如果去重的元素是元组,或者自定义类的实例,只要对象是可哈希的就可以直接用set去重:
# 元组列表去重
tuple_list = [(1, 2), (3, 4), (1, 2), (5, 6)]
result = list(set(tuple_list))
print(result)
# 自定义可哈希对象去重
class Student:
def __init__(self, sid, name):
self.sid = sid
self.name = name
def __hash__(self):
return hash(self.sid)
def __eq__(self, other):
if not isinstance(other, Student):
return False
return self.sid == other.sid
students = [Student(1, "张三"), Student(2, "李四"), Student(1, "张三")]
result_students = list(set(students))
for s in result_students:
print(s.sid, s.name)
2. 不可哈希对象的去重
如果要去重的元素是列表这类不可哈希对象,可以先将元素转换为可哈希的类型,比如转成元组后再去重:
# 列表元素为列表的去重 list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6]] # 先转成元组再去重,再转回列表 temp_set = set(tuple(item) for item in list_of_lists) result = [list(item) for item in temp_set] print(result)
不同去重方式的性能对比
我们可以通过测试对比列表遍历去重和set去重的效率差异:
import time
# 生成测试数据,1万个元素的列表,包含一半重复值
test_data = list(range(5000)) * 2
# 列表遍历去重
start = time.time()
result1 = []
for item in test_data:
if item not in result1:
result1.append(item)
print("列表遍历去重耗时:", time.time() - start)
# set去重
start = time.time()
result2 = list(set(test_data))
print("set去重耗时:", time.time() - start)
在实际测试中,列表遍历去重的时间会随着数据量增大显著上升,而set去重的耗时增长非常平缓,当数据量达到10万级别时,set去重的效率会比列表遍历去重高几十倍甚至上百倍。
set去重的注意事项
- set中的元素必须是可哈希的,不可哈希的对象无法直接放入set
- set是无序的,去重后元素顺序和原始顺序可能不一致,需要保留顺序要采用额外方案
- 如果两个对象的哈希值相同但内容不相等,set会同时存储这两个对象,这是哈希冲突的正常处理情况
- 对于超大数据的去重,set会占用较多内存,因为要存储哈希表结构,如果对内存敏感可以考虑其他方案