Python列表去重5种实现方法:保序、性能与不可哈希处理
Python 列表去重看起来简单,实际选型主要看两个条件:一是结果是否必须保持元素首次出现的顺序,二是列表里的元素是否可哈希。像 [, , ] 这种嵌套列表就属于不可哈希元素,直接交给 set 或 dict 会报错。下面按这两个条件,把 5 种解法逐一说明,并给出函数实现、调用示例和复杂度判断。1. dict.fromkeys:保序去重的首选
从 Python 3.7 开始,dict 会保留键的插入顺序,因此可以用 dict.fromkeys(lst) 先得到去重后的字典,再用 list() 取出所有键。字典键本身唯一,这一过程的时间复杂度为 O(n),写法也最简洁。
def remove_duplicates_ordered(lst):
return list(dict.fromkeys(lst))
my_list =
print(remove_duplicates_ordered(my_list))
# 输出:
函数只做两步:dict.fromkeys 创建以列表元素为键的字典,list 把字典键转回列表。因为 dict 在 3.7 及之后保证插入顺序,所以结果会按元素第一次出现的顺序排列。日常开发中如果元素可哈希且要求保序,这是优先选择。
2. set 集合:最快但不保序
如果完全不关心顺序,只要求去重,set 是最短路径。集合天然不允许重复元素,转成 list 后即可使用。它的时间复杂度同样为 O(n),但不保证原始顺序,实际输出顺序由哈希决定。
def remove_duplicates_unordered(lst):
return list(set(lst))
my_list =
print(remove_duplicates_unordered(my_list))
# 输出顺序不确定,例如
这个方法代码量最少,适合只想要去重结果、不依赖顺序的场景。如果后续逻辑依赖元素先后次序,就不要用 set 方案。
3. 辅助集合 seen:保序且可插入自定义逻辑
需要保序、又想在去重过程中加入自定义判断时,可以用辅助集合 seen 配合结果列表。遍历原列表,元素第一次出现时追加到 result,同时写入 seen。由于集合查询是 O(1),整体仍为 O(n),并且写法兼容较老的 Python 版本。
def remove_duplicates_seen(lst):
seen = set()
result = []
for item in lst:
if item not in seen:
seen.add(item)
result.append(item)
return result
这种方法的价值在于控制力更强。比如可以在 if 条件里加过滤空值、大小写归一、类型转换等处理,再去决定是否加入结果列表。
4. 纯列表遍历:处理不可哈希元素
当列表元素本身不可哈希时,例如嵌套列表 [, , ],set 和 dict 方案都会报错。此时只能退回到纯列表遍历,用 in 检查结果列表中是否已经存在相同元素。因为每次 in 都要扫描 result,时间复杂度为 O(n²),数据量大时性能下降明显,仅适合小规模数据。
def remove_duplicates_unhashable(lst):
result = []
for item in lst:
if item not in result:
result.append(item)
return result
nested = [, , ]
print(remove_duplicates_unhashable(nested))
# 输出: [, ]
选择这个方案前,先确认元素确实不可哈希。如果只是普通数字、字符串、元组,仍然应该用基于哈希的方法。
5. Pandas:数据清洗场景的 drop_duplicates
如果项目已经用 Pandas 做数据清洗,可以直接把列表转成 Series,再调用 drop_duplicates()。该方法默认保留首次出现的顺序,并且对大规模数据、混合类型以及包含 NaN 的场景支持较好。代价是要引入 Pandas 依赖,不适合只为列表去重而专门安装。
import pandas as pd
def remove_duplicates_pandas(lst):
return pd.Series(lst).drop_duplicates().tolist()
这段函数把列表包装成 Series,调用 drop_duplicates 去重,最后用 tolist 转回 Python 列表。适合数据分析流程中顺带完成去重。
选型建议与复杂度对照
把 5 种方案放在一起,可以按下面思路决策:
- 日常开发、要求保序、元素可哈希:dict.fromkeys(),O(n)。
- 完全不关心顺序、只追求速度:set(),O(n)。
- 需要保序并插入自定义过滤或转换逻辑,或兼容非常老的 Python:辅助集合 seen,O(n)。
- 元素不可哈希,例如嵌套列表:纯列表遍历,O(n²),仅小规模使用。
- 已经在使用 Pandas、做数据清洗、数据量大或包含 NaN:pd.Series().drop_duplicates(),默认保留首次出现顺序。
原文给出的总结同样清晰:日常首选方法 1,即 dict.fromkeys;不在乎顺序只求快,用方法 2 的 set;遇到嵌套列表报错,用方法 4 的纯列表遍历。理解保序需求和可哈希边界,就能在这几种写法之间快速选对方案。
Re: Python列表去重5种实现方法:保序、性能与不可哈希处理
写得很清楚,把“是否保序”和“元素是否可哈希”这两个判断条件拎出来之后,选型就不容易错了。我平时最常用的也是 dict.fromkeys,尤其是从接口拿到列表后去重,既保序又简洁;set 只在完全不在乎顺序时用。嵌套列表那个坑确实容易踩,一用 set 就报 unhashable,退回纯列表遍历虽然 O(n²),但小数据量下最直接。Pandas 方案适合本来就在做数据清洗的项目,单独为去重引入依赖就不太划算。感谢整理,收藏了。Re: Python列表去重5种实现方法:保序、性能与不可哈希处理
感谢分享,整理得很清楚。用“是否必须保序”和“元素是否可哈希”这两个条件来选型,确实一下就抓到重点了。 我平时也基本是这样:日常优先 dict.fromkeys(),写法短、保序还稳定;如果完全不在乎顺序,set 最省事;要是去重中间还想加过滤或转换逻辑,辅助集合 seen 会更灵活;遇到嵌套列表这种不可哈希元素,就只能用纯列表遍历,虽然 O(n²) 但小数据量够用;如果本来就在做 Pandas 数据清洗,drop_duplicates() 也很顺手。 最后那段选型建议总结得挺实用,以后遇到列表去重可以直接按这个思路对照着选。Re: Python列表去重5种实现方法:保序、性能与不可哈希处理
这篇总结得很实用,把去重问题拆成“是否保序”和“元素是否可哈希”两个判断点,选型就不容易乱了。日常可哈希又保序,dict.fromkeys 确实最顺手;只求去重不管顺序,set 最省事;要在去重时加过滤或转换,seen 那套更好扩展;嵌套列表这种不可哈希的,只能退回列表遍历,但得注意 O(n²) 在小数据量下用。Pandas 的 drop_duplicates 适合数据清洗场景,不过专门为列表去重引入依赖就不划算。收藏了。
页:
[1]