在 Python 中,dict.fromkeys() 和 OrderedDict.fromkeys() 的去重逻辑核心依赖 "字典的键(key)必须唯一" 这一特性,本质是 "用键的唯一性过滤重复元素",仅在 "是否保留插入顺序" 上有差异(Python 3.7+ 后差异已极小)。以下从「原理拆解、底层实现、去重步骤」三方面详细说明:
一、核心前提:字典的键(key)特性
无论是普通 dict 还是 OrderedDict,都遵循两个关键规则:
-
键的唯一性 :字典中不能存在重复的 key,若重复赋值(如
d = {1: None, 1: None}),后定义的 key 会覆盖前一个(最终仅保留 1 个); -
插入顺序保留(Python 3.7+):
- 普通
dict从 Python 3.7 开始,官方保证保留键的插入顺序(底层实现优化); OrderedDict是 Python 2.7+ 就存在的有序字典,专门设计用于 "严格保留插入顺序"(Python 3.7+ 后与普通dict的顺序特性一致,但仍有额外功能如move_to_end())。
- 普通
这两个规则是 "去重并保留顺序" 的核心基础。
二、dict.fromkeys() 的去重原理与实现
1. 函数功能与语法
dict.fromkeys(iterable, value=None) 是字典的类方法,作用是:
- 接收一个可迭代对象 (如列表、元组),将其每个元素作为字典的
key; - 所有 key 对应的
value默认为None(可通过第二个参数指定统一值); - 返回一个新字典。
语法示例:
python
运行
ini
lst = [1, 2, 2, 3, 3, 3]
d = dict.fromkeys(lst)
print(d) # 输出:{1: None, 2: None, 3: None}(去重且保留插入顺序)
2. 去重的底层实现步骤
dict.fromkeys() 本质是 "遍历可迭代对象,逐个往字典中添加 key",去重逻辑隐含在遍历过程中,具体步骤:
-
初始化空字典:创建一个空的哈希表(字典底层数据结构),用于存储 key-value 对;
-
遍历可迭代对象 :按顺序取出
iterable中的每个元素(如列表[1,2,2,3]依次取 1、2、2、3); -
判断 key 是否已存在:
- 对每个元素,计算其哈希值(
hash(key)),通过哈希表快速查找是否已存在该 key(查找效率 O (1)); - 若 key 不存在:将其插入字典(保留当前顺序),value 设为默认值
None; - 若 key 已存在:直接跳过,不重复插入(这就是去重的核心);
- 对每个元素,计算其哈希值(
-
返回字典:遍历结束后,字典中的 key 就是 "去重后且保留原插入顺序" 的结果。
3. 去重示例拆解(对应方式 1 的代码)
方式 1 中 filtered = [1,4,6,7,7](筛选后未去重的列表),执行 dict.fromkeys(filtered):
python
运行
ini
filtered = [1,4,6,7,7]
d = dict.fromkeys(filtered)
- 遍历第 1 个元素
1:哈希表中无1,插入 → 字典:{1: None}; - 遍历第 2 个元素
4:哈希表中无4,插入 → 字典:{1: None, 4: None}; - 遍历第 3 个元素
6:哈希表中无6,插入 → 字典:{1: None, 4: None, 6: None}; - 遍历第 4 个元素
7:哈希表中无7,插入 → 字典:{1: None, 4: None, 6: None, 7: None}; - 遍历第 5 个元素
7:哈希表中已存在7,跳过 → 字典无变化; - 最终字典的 key 为
[1,4,6,7],转列表后就是去重结果。
4. 关键特点
- 去重逻辑:依赖 "key 唯一",重复元素直接跳过,无显式比较(效率高);
- 顺序保留:Python 3.7+ 保证插入顺序,与原列表顺序一致;
- 效率:遍历 + 哈希查找,时间复杂度 O (m)(m 为
filtered列表长度)。
三、OrderedDict.fromkeys() 的去重原理与实现
1. 与普通 dict.fromkeys() 的区别
OrderedDict 是 collections 模块中的有序字典,其 fromkeys() 方法功能、语法、去重逻辑与普通 dict.fromkeys() 完全一致,唯一差异是:
- 顺序保证的兼容性 :Python 3.7 之前,普通
dict不保证插入顺序,而OrderedDict从诞生起就严格保留插入顺序; - 额外功能 :
OrderedDict提供move_to_end()、popitem(last=True/False)等顺序相关方法(去重场景用不到)。
2. 去重实现步骤
与普通 dict.fromkeys() 完全相同,仅底层存储结构略有差异(OrderedDict 内部用 "双向链表" 记录插入顺序,而 Python 3.7+ 普通 dict 用 "插入顺序数组" 记录),但对用户而言,去重效果一致:
python
运行
scss
from collections import OrderedDict
filtered = [1,4,6,7,7]
od = OrderedDict.fromkeys(filtered)
print(od) # 输出:OrderedDict([(1, None), (4, None), (6, None), (7, None)])
print(list(od.keys())) # 输出:[1,4,6,7](去重且保留顺序)
3. 适用场景
- Python 3.6 及以下版本:需 "去重 + 保留顺序" 时,必须用
OrderedDict.fromkeys()(普通dict无序); - Python 3.7 及以上版本:普通
dict.fromkeys()已能满足 "去重 + 保留顺序",无需再用OrderedDict(除非需要其额外的顺序操作方法)。
四、两者对比与总结
| 特性 | dict.fromkeys() |
OrderedDict.fromkeys() |
|---|---|---|
| 去重原理 | 依赖 key 唯一性,重复 key 跳过 | 依赖 key 唯一性,重复 key 跳过 |
| 顺序保留(Python 3.7+) | 是 | 是 |
| 顺序保留(Python <3.7) | 否 | 是 |
| 底层效率 | 略高(结构更简单) | 略低(双向链表额外开销) |
| 依赖 | 无(内置 dict) | 需导入 collections.OrderedDict |
| 适用场景 | Python 3.7+ 日常去重 + 保序 | Python <3.7 或需额外顺序操作的场景 |
五、关键补充:为什么不用 set 去重?
可能有疑问:"既然 set 也能去重,为什么还要用 dict.fromkeys()?"------ 核心差异是「是否保留顺序」:
set是无序结构,去重后会打乱原列表顺序(如list(set([1,4,6,7,7]))可能输出[1,6,4,7]);dict.fromkeys()和OrderedDict.fromkeys()去重后严格保留原列表的插入顺序(这是方式 1 的核心需求)。
例如:
python
运行
less
filtered = [4,1,6,7,7] # 原顺序是 4→1→6→7
print(list(set(filtered))) # 输出:[1,4,6,7](顺序打乱)
print(list(dict.fromkeys(filtered).keys())) # 输出:[4,1,6,7](顺序保留)
最终总结
dict.fromkeys() 和 OrderedDict.fromkeys() 的去重逻辑本质是:
- 利用 "字典 key 不可重复" 的特性,遍历元素时跳过已存在的 key;
- 保留元素的 "首次插入顺序"(Python 3.7+ 普通 dict 也支持);
- 去重效率高(O (m)),且能兼顾顺序,是 "去重 + 保序" 的最优方案之一。
方式 1 中用 dict.fromkeys(),正是因为它能高效完成 "筛选后列表的去重 + 保序",且语法简洁、无需额外依赖