dict.fromkeys() 和 OrderedDict.fromkeys() 的底层实现,核心都是 "利用字典键的唯一性构建映射" ,但因 dict 和 OrderedDict 的底层存储结构不同(前者侧重高效哈希查找,后者侧重严格顺序保留),导致实现细节有差异。以下从「通用逻辑→各自底层实现→差异对比」三方面,结合 Python 源码级原理拆解(以 CPython 为准):
一、先明确:两者的通用实现逻辑
无论 dict 还是 OrderedDict,其 fromkeys() 方法的核心流程完全一致(类方法,无状态依赖),仅底层 "存储键值对 + 记录顺序" 的方式不同:
-
接收参数处理:
- 第一个参数
iterable:必须是可迭代对象(如列表、元组),遍历其元素作为字典的key; - 第二个参数
value:可选,默认None,所有key共享同一个value(注意:若value是可变对象如列表,所有 key 会引用同一个对象,而非复制)。
- 第一个参数
-
初始化容器 :创建一个空的
dict或OrderedDict实例(底层存储结构不同)。 -
遍历迭代对象:
- 按顺序取出
iterable中的每个元素; - 对每个元素,判断是否已作为
key存在于容器中(通过哈希查找,O (1) 效率); - 若不存在:插入容器(
key为当前元素,value为传入的默认值); - 若已存在:跳过(保证 key 唯一性,实现去重)。
- 按顺序取出
-
返回容器实例:遍历结束后,返回构建好的字典。
关键共性 :fromkeys() 是 "类方法"(@classmethod),不依赖实例状态,直接创建新字典;value 是 "共享引用"(而非每个 key 复制一个 value),例如:
python
运行
scss
d = dict.fromkeys([1,2,3], [])
d[1].append(4)
print(d) # {1: [4], 2: [4], 3: [4]}(所有 key 共享同一个列表)
二、dict.fromkeys() 的底层实现(CPython)
Python 中的普通 dict 底层是 "哈希表 + 插入顺序数组" (Python 3.7+ 优化后),兼顾高效查找和顺序保留。fromkeys() 作为 dict 的类方法,底层由 C 语言实现(Objects/dictobject.c),核心步骤如下:
1. 底层数据结构(dict 的核心存储)
普通 dict 内部有三个关键结构(简化模型):
- 哈希表(Hash Table) :本质是数组(称为
ma_table),每个元素是dict_entry结构体(存储key指针、value指针、哈希值hash、下一个冲突元素指针next); - 插入顺序数组(Insertion Order Array) :数组
ma_order,存储dict_entry的指针,按插入顺序排列(Python 3.7+ 新增,用于保留顺序); - 哈希冲突解决 :采用 "开放寻址法 + 链表"(早期是链表法,3.11 后优化为开放寻址法,冲突元素通过
next指针串联)。
2. dict.fromkeys() 的 C 语言实现流程(简化)
c
运行
scss
// 伪代码示意(基于 CPython 3.11)
static PyObject * dict_fromkeys(PyTypeObject *type, PyObject *iterable, PyObject *value) {
// 1. 创建空 dict 实例(分配哈希表和顺序数组内存)
PyObject *new_dict = PyDict_New();
if (new_dict == NULL) return NULL; // 内存分配失败
// 2. 遍历可迭代对象 iterable
PyObject *it = PyObject_GetIter(iterable);
if (it == NULL) { Py_DECREF(new_dict); return NULL; }
PyObject *key;
while ((key = PyIter_Next(it)) != NULL) {
// 3. 计算 key 的哈希值(需保证 key 是不可变类型,否则报错)
Py_hash_t hash = PyObject_Hash(key);
if (hash == -1) { // 哈希计算失败(如 key 是列表)
Py_DECREF(key);
Py_DECREF(it);
Py_DECREF(new_dict);
return NULL;
}
// 4. 查找 key 是否已存在于哈希表中(O(1) 效率)
PyDictKeyEntry *entry = PyDict_GetEntry(new_dict, key, hash);
if (entry == NULL) { // key 不存在,插入新 entry
// 4.1 向哈希表插入 key(处理冲突)
if (PyDict_SetItemWithHash(new_dict, key, value, hash) < 0) {
Py_DECREF(key);
Py_DECREF(it);
Py_DECREF(new_dict);
return NULL;
}
// 4.2 向插入顺序数组 ma_order 添加 entry 指针(保留顺序)
PyDict_AppendOrder(new_dict, entry);
}
Py_DECREF(key); // 释放 key 的临时引用
}
Py_DECREF(it);
return new_dict;
}
3. 核心细节
- 哈希计算 :
key必须是不可变类型(如 int、str、tuple),否则PyObject_Hash(key)会报错(这也是字典 key 不可变的底层原因); - 查找效率 :通过哈希值定位哈希表索引,冲突时通过
next指针遍历冲突链表,平均查找效率 O (1); - 顺序保留 :Python 3.7+ 后,
dict新增ma_order数组,插入新 key 时,将其dict_entry指针追加到数组末尾,遍历字典时按数组顺序迭代,实现 "插入顺序保留"; - 内存分配:哈希表是动态扩容的(负载因子达到 2/3 时,扩容为原大小的 2 倍),避免哈希冲突过多导致效率下降。
三、OrderedDict.fromkeys() 的底层实现(CPython)
OrderedDict 是 collections 模块的有序字典,底层是 "哈希表 + 双向链表" 结构(Python 3.7+ 后,普通 dict 也支持顺序,但 OrderedDict 保留额外的顺序操作功能)。其 fromkeys() 方法继承自 dict,但底层存储和顺序维护逻辑不同。
1. 底层数据结构(OrderedDict 的核心存储)
OrderedDict 内部包含两个关键部分:
- 哈希表(同普通 dict) :用于
key的快速查找(dict_entry结构体),保证 O (1) 查找效率; - 双向链表(Doubly Linked List) :每个
dict_entry额外包含prev和next指针,串联所有key,严格记录插入顺序(这是与普通dict的核心区别)。
2. OrderedDict.fromkeys() 的实现流程(简化)
OrderedDict 没有重写 fromkeys() 方法,直接复用 dict.fromkeys() 的逻辑,但在 key 插入时,会通过 OrderedDict 重写的 __setitem__ 方法,额外维护双向链表:
c
运行
ini
// 伪代码示意(基于 CPython 3.11)
// OrderedDict 继承自 dict,复用 fromkeys(),但重写了 PyDict_SetItemWithHash
static int odict_setitem(PyObject *self, PyObject *key, PyObject *value, Py_hash_t hash) {
// 1. 调用普通 dict 的插入逻辑(哈希表插入 key-value)
int res = PyDict_SetItemWithHash(self, key, value, hash);
if (res != 0) return res;
// 2. 额外维护双向链表(OrderedDict 的核心)
PyDictKeyEntry *entry = PyDict_GetEntry(self, key, hash);
OrderedDictObject *odict = (OrderedDictObject *)self;
// 2.1 将新 entry 追加到双向链表尾部(保留插入顺序)
entry->prev = odict->tail;
entry->next = NULL;
if (odict->tail != NULL) {
odict->tail->next = entry;
} else {
odict->head = entry; // 链表为空时,entry 作为头节点
}
odict->tail = entry; // 更新尾节点
return 0;
}
3. 核心细节
- 复用 dict 基础功能 :
OrderedDict是dict的子类,哈希表的查找、扩容、冲突处理逻辑完全复用普通dict,仅新增双向链表维护顺序; - 双向链表的开销 :每个
dict_entry多占用prev和next两个指针的内存,且插入 / 删除时需额外维护链表指针(时间开销略高于普通dict); - 顺序操作支持 :双向链表使得
OrderedDict能高效实现move_to_end()、popitem(last=True/False)等顺序相关操作(普通dict不支持)。
四、两者底层实现的核心差异
| 对比维度 | dict.fromkeys()(Python 3.7+) |
OrderedDict.fromkeys() |
|---|---|---|
| 底层存储结构 | 哈希表 + 插入顺序数组(ma_order) |
哈希表 + 双向链表 |
| 顺序维护逻辑 | 数组追加(插入时记录 entry 指针顺序) |
双向链表串联(prev/next 指针) |
| 顺序操作支持 | 仅保留插入顺序,无额外操作 | 支持 move_to_end()、popitem() 等 |
| 内存开销 | 较低(数组存储指针,结构简单) | 较高(双向链表额外占用 prev/next 指针) |
| 插入 / 删除效率 | 略高(仅数组追加,无链表操作) | 略低(需维护链表指针) |
| 继承关系 | 内置 dict 的类方法 |
继承 dict,重写 __setitem__ 维护顺序 |
五、关键补充:Python 3.7+ 后两者的底层变化
Python 3.7 是一个关键版本,普通 dict 官方承诺 "保留插入顺序",导致 OrderedDict 的定位发生变化:
- 普通 dict 的优化 :新增
ma_order数组记录插入顺序,底层由 "纯哈希表" 变为 "哈希表 + 顺序数组",兼顾效率和顺序; - OrderedDict 的定位 :不再是 "唯一支持顺序的字典",而是 "支持更多顺序操作的字典"(如
move_to_end()、按插入顺序反向迭代等); - 性能差异 :普通
dict的fromkeys()因结构更简单(数组 vs 双向链表),插入和遍历效率略高于OrderedDict。
六、底层实现带来的实际影响
- value 共享引用 :两者的
fromkeys()都让所有 key 共享同一个value(底层仅存储一个value指针),若value是可变对象(如列表),修改一个 key 的value会影响所有 key; - key 必须不可变 :底层依赖
hash(key)计算索引,可变对象(列表、字典)无法计算稳定哈希值,会报错; - 去重逻辑:均通过 "哈希表查找 key 是否存在" 实现去重,重复 key 直接跳过,效率 O (1);
- 顺序稳定性 :Python 3.7+ 后,两者都能保留插入顺序,但
OrderedDict的顺序在所有 Python 版本中都稳定(包括 3.7 之前)。
最终总结
dict.fromkeys() 和 OrderedDict.fromkeys() 的底层实现原理可概括为:
- 共性:均基于 "哈希表" 实现 key 的快速查找,利用 "key 唯一性" 去重,通过遍历可迭代对象构建字典;
- 差异 :普通
dict用 "插入顺序数组" 维护顺序(高效低耗),OrderedDict用 "双向链表" 维护顺序(支持更多操作但有额外开销); - 本质 :
fromkeys()是 "批量构建字典" 的语法糖,底层核心是哈希表的插入逻辑,顺序维护由字典自身的存储结构决定。
理解底层后,就能明白:为什么 Python 3.7+ 日常场景优先用 dict.fromkeys()(高效低耗),而 OrderedDict 仅在需要额外顺序操作时使用。