Python 列表去重看起来简单,实际选型主要看两个条件:一是结果是否必须保持元素首次出现的顺序,二是列表里的元素是否可哈希。像 [[1, 2], [1, 2], [3]] 这种嵌套列表就属于不可哈希元素,直接交给 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 = [3, 1, 4, 1, 5, 9, 2, 6, 5]
- print(remove_duplicates_ordered(my_list))
- # 输出: [3, 1, 4, 5, 9, 2, 6]
复制代码
函数只做两步:dict.fromkeys 创建以列表元素为键的字典,list 把字典键转回列表。因为 dict 在 3.7 及之后保证插入顺序,所以结果会按元素第一次出现的顺序排列。日常开发中如果元素可哈希且要求保序,这是优先选择。
2. set 集合:最快但不保序
如果完全不关心顺序,只要求去重,set 是最短路径。集合天然不允许重复元素,转成 list 后即可使用。它的时间复杂度同样为 O(n),但不保证原始顺序,实际输出顺序由哈希决定。
- def remove_duplicates_unordered(lst):
- return list(set(lst))
- my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
- print(remove_duplicates_unordered(my_list))
- # 输出顺序不确定,例如 [1, 2, 3, 4, 5, 6, 9]
复制代码
这个方法代码量最少,适合只想要去重结果、不依赖顺序的场景。如果后续逻辑依赖元素先后次序,就不要用 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. 纯列表遍历:处理不可哈希元素
当列表元素本身不可哈希时,例如嵌套列表 [[1, 2], [1, 2], [3]],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 = [[1, 2], [1, 2], [3]]
- print(remove_duplicates_unhashable(nested))
- # 输出: [[1, 2], [3]]
复制代码
选择这个方案前,先确认元素确实不可哈希。如果只是普通数字、字符串、元组,仍然应该用基于哈希的方法。
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 的纯列表遍历。理解保序需求和可哈希边界,就能在这几种写法之间快速选对方案。 |