除了 dict.fromkeys() 和 OrderedDict.fromkeys(),Python 中还有多种列表去重方法,核心差异集中在 是否保留原顺序、时间复杂度、内存占用 三个维度。以下是 6 种常用方法,按 "日常实用性 + 效率" 排序,附带原理、代码示例和场景适配:
一、核心前提:去重的两大核心需求
- 需求 1:保留原顺序(日常开发最常用,如去重后仍保持元素首次出现的顺序);
- 需求 2:无需保留顺序(追求极致效率或不关心顺序,如数据统计、去重后排序)。
所有方法均围绕这两个需求展开,底层逻辑本质是 "通过容器记录已出现的元素,过滤重复项"。
二、6 种列表去重方法详解
方法 1:列表推导式 + 辅助集合(推荐,保序 + 高效)
原理
- 用 列表推导式 遍历原列表,保留首次出现的元素;
- 用 辅助集合 (
seen)记录已出现的元素(集合查找效率 O (1),比列表查找快); - 核心逻辑:遍历元素时,若元素不在
seen中,则保留并加入seen;否则跳过。
代码示例
python
运行
ini
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
seen = set()
result = [x for x in lst if x not in seen and not seen.add(x)]
print(result) # 输出:[1, 2, 3, 4, 5](保留原顺序)
关键细节
not seen.add(x):set.add(x)无返回值(返回None),not None为True,因此可简化为 "判断 + 添加" 一步完成;- 时间复杂度:O (n)(遍历列表 O (n) + 集合查找 O (1));
- 内存占用:O (n)(辅助集合存储所有不重复元素)。
优点
- 保留原顺序,效率高(比
dict.fromkeys()更灵活,可自定义过滤逻辑); - 支持异构列表(如包含字符串、元组等不可变元素)。
适用场景
- 日常开发中 "保序去重" 的核心场景(替代
dict.fromkeys(),逻辑更直观)。
方法 2:set 转列表(最快,无需保序)
原理
- 利用 集合(set)的元素唯一性,直接将列表转为集合(自动去重),再转回列表;
- 集合是无序结构,因此去重后会打乱原顺序。
代码示例
python
运行
scss
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = list(set(lst))
print(result) # 输出:[1, 2, 3, 4, 5](顺序不固定,如 [2,1,3,4,5])
关键细节
- 时间复杂度:O (n)(集合插入和转换均为线性时间,底层哈希表优化);
- 内存占用:O (n)(集合存储不重复元素);
- 限制:仅支持 不可变元素 (如 int、str、tuple),若列表包含可变元素(如列表、字典),会报错(
TypeError: unhashable type: 'list')。
优点
- 代码极简(一行完成),速度最快(CPython 底层优化,比纯 Python 逻辑快一个量级);
- 自动去重 + 去重后可按需排序(如
sorted(list(set(lst))))。
适用场景
- 无需保留原顺序,且元素为不可变类型(如数据统计、去重后排序)。
方法 3:pandas 库 drop_duplicates()(大数据量 + 保序)
原理
- 利用
pandas的Series数据结构,通过drop_duplicates()方法去重; - 底层用 C 语言优化,大数据量(十万级以上)效率远超纯 Python 方法。
代码示例
python
运行
scss
import pandas as pd
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5] * 1000 # 模拟 9000 个元素的大数据量
result = pd.Series(lst).drop_duplicates().tolist()
print(result) # 输出:[1, 2, 3, 4, 5](保留原顺序)
关键细节
- 时间复杂度:O (n)(底层哈希表实现,线性时间);
- 内存占用:O (n)(
Series存储数据); - 优势:支持缺失值(
NaN)去重,且大数据量下比纯 Python 方法快 10~100 倍。
优点
- 大数据量处理高效,保留原顺序;
- 支持复杂数据类型(如
datetime、object类型)。
缺点
- 需安装
pandas库(非 Python 内置); - 小数据量下有库调用开销,效率不如方法 1、2。
适用场景
- 数据分析、大数据处理(如 CSV 数据、日志数据去重)。
方法 4:遍历 + 条件判断(无额外容器,保序)
原理
- 不使用集合,仅通过列表的
in操作判断元素是否已存在; - 遍历原列表,若元素不在结果列表中,则追加到结果列表。
代码示例
python
运行
ini
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = []
for x in lst:
if x not in result: # 列表查找效率 O(k),k 为 result 长度
result.append(x)
print(result) # 输出:[1, 2, 3, 4, 5](保留原顺序)
关键细节
- 时间复杂度:O (n²)(遍历原列表 O (n) + 列表查找 O (k),k ≤ n);
- 内存占用:O (n)(仅结果列表,无额外容器)。
优点
- 无需额外依赖(纯 Python 原生逻辑),无哈希表限制(支持可变元素?不 ------ 列表
in操作不依赖哈希,但若元素是可变类型如列表,仍可判断,例如[1, [2]]去重)。
缺点
- 效率极低(列表查找是线性时间,列表越长越慢)。
适用场景
- 列表长度极小(如几十个元素),且不希望引入集合(如面试题要求 "不用 set 去重")。
方法 5:collections.Counter(统计频次 + 去重,保序)
原理
Counter是字典的子类,用于统计元素出现频次;Counter.keys()会保留元素的 "首次出现顺序"(Python 3.7+),因此可通过keys()快速去重。
代码示例
python
运行
scss
from collections import Counter
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = list(Counter(lst).keys())
print(result) # 输出:[1, 2, 3, 4, 5](保留原顺序)
关键细节
- 时间复杂度:O (n)(统计频次 O (n) + 提取 keys O (m),m 为不重复元素个数);
- 内存占用:O (n)(存储元素频次);
- 额外功能:可同时获取元素出现次数(如
Counter(lst)[2]得到 2 的出现次数 2)。
优点
- 保序去重的同时,可获取频次统计;
- 语法简洁,支持不可变元素。
适用场景
- 去重 + 频次统计的复合场景(如 "找出列表中不重复的元素,并统计每个元素出现次数")。
方法 6:itertools.groupby(需先排序,无需保序)
原理
groupby用于将迭代对象中连续的重复元素分组;- 需先对列表排序(使重复元素连续),再提取每组的第一个元素,实现去重;
- 因排序会打乱原顺序,因此适合无需保序的场景。
代码示例
python
运行
ini
from itertools import groupby
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
# 步骤 1:排序(使重复元素连续)
lst_sorted = sorted(lst)
# 步骤 2:groupby 分组,提取每组第一个元素
result = [key for key, group in groupby(lst_sorted)]
print(result) # 输出:[1, 2, 3, 4, 5](排序后的顺序)
关键细节
- 时间复杂度:O (n log n)(排序占主导,
groupby遍历 O (n)); - 内存占用:O (n)(排序和分组的临时空间);
- 限制:必须先排序,因此无法保留原顺序。
优点
- 可同时处理 "排序 + 去重",适合需要排序后去重的场景;
- 支持自定义排序规则(如
sorted(lst, key=lambda x: -x)倒序后去重)。
适用场景
- 需先排序再去重的场景(如 "对成绩列表去重,并按从高到低排序")。
三、各方法对比与场景选择
| 方法 | 是否保序 | 时间复杂度 | 内存占用 | 核心优势 | 适用场景 |
|---|---|---|---|---|---|
| 列表推导式 + 辅助集合 | 是 | O(n) | O(n) | 灵活高效,保序,支持异构元素 | 日常保序去重(首选) |
set 转列表 |
否 | O(n) | O(n) | 代码极简,速度最快 | 无需保序,元素为不可变类型 |
pandas.drop_duplicates() |
是 | O(n) | O(n) | 大数据量高效,支持复杂类型 | 数据分析、十万级以上元素去重 |
| 遍历 + 条件判断 | 是 | O(n²) | O(n) | 纯原生逻辑,无额外依赖 | 列表极短,面试题场景 |
Counter.keys() |
是 | O(n) | O(n) | 去重 + 频次统计 | 需统计元素出现次数的场景 |
itertools.groupby |
否(排序后) | O(n log n) | O(n) | 排序 + 去重一体化 | 需先排序再去重的场景 |
四、避坑重点
-
可变元素去重 :若列表包含可变元素(如
[1, [2], [2], 3]),set、dict.fromkeys()、Counter会报错(因可变元素不可哈希),此时只能用 "遍历 + 条件判断"(方法 4):python
运行
csslst = [1, [2], [2], 3] result = [] for x in lst: if x not in result: result.append(x) print(result) # 输出:[1, [2], 3](正确去重) -
原列表修改 :以上方法均生成新列表,若需修改原列表,可直接赋值(如
lst = [x for x in lst if x not in seen and not seen.add(x)]); -
空列表 / 单一元素:所有方法均兼容空列表(返回空)和单一元素(返回自身),无异常;
-
性能误区:"遍历 + 条件判断"(方法 4)虽无额外容器,但列表查找 O (n) 导致效率极低,列表长度超过 1000 时不推荐使用。
五、总结
- 日常保序去重 :优先选「列表推导式 + 辅助集合」(灵活高效)或「
dict.fromkeys()」(语法简洁); - 无需保序去重 :优先选「
set转列表」(速度最快); - 大数据量去重 :优先选「
pandas.drop_duplicates()」(底层 C 优化); - 复合需求 :需频次统计用「
Counter」,需排序用「groupby」。
根据 "是否保序、数据量、是否需要额外功能" 选择合适的方法,即可兼顾效率和可读性。