1. 为什么字典比列表查询快
首先,请看下面这段代码
|
|
直接运行:
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
['aa', 'cc', 'ee']
24.5699999332
去掉注释 data = dict.fromkeys(data,True)
后:
{'a': True, 'c': True, 'b': True, 'e': True, 'd': True, 'g': True, 'f': True, 'i': True, 'h': True, 'k': True, 'j': True, 'm': True, 'l': True, 'o': True, 'n': True, 'q': True, 'p': True, 's': True, 'r': True, 'u': True, 't': True, 'w': True, 'v': True, 'y': True, 'x': True, 'z': True}
['aa', 'cc', 'ee']
17.8080000877
反复测试多次,结果不会偏差超过 1 秒。为什么将列表通过 dict.fromkeys 函数转换为字典之后,运行速度会明显加快呢?
Python 字典中使用了 hash table,因此查找操作的复杂度为 O(1),而 list 实际是个数组,在 list 中,查找需要遍历整个 list,其复杂度为 O(n),因此对成员的查找访问等操作字典要比 list 更快。下面,我们一起来看下 Python 源码中列表和字典结构是如何实现的?
2. Python 源码中的常见对象
Python 中的对象都继承自 PyObject 或 PyVarObject。继承自 PyObject 的对象长度固定,比如 int 等。继承自 PyVarObject 的对象长度可变,比如 list、dict等。而 PyObject 和 PyVarObject 拥有相同的头部 PyObject_HEAD。
- PyObject_HEAD
定义于 include/object.h 中
|
|
_PyObject_HEAD_EXTRA
是双向链表结构,用于垃圾回收。ob_refcnt
是对象的引用计数,当没有被引用时,自动回收内存。ob_type
是指向类型对象的指针,决定了对象的类型。
- PyObject
定义于 include/object.h 中
|
|
- PyVarObject
定义于 include/object.h 中
|
|
PyVarObject 包含一组对象,数量由 ob_size
指定。
3. Python 中列表的实现
- 定义
include/listobject.h
|
|
- 内置函数
Objects/listobject.c
|
|
在定义列表的文件中,定义了 Python 中 list 内置方法对应的 C 函数。下面简单看下这些函数的实现逻辑。
- 初始化
Objects/listobject.c
|
|
- append
调用逻辑:listappend -> app1 ->list_resize
+1 后,PyList_SET_ITEM
include/listobject.h
|
|
Objects/listobject.c
|
|
list_resize()
会多申请一些空间以避免频繁地内存操作,增长趋势是:0,4, 8,16,25,35,46,58,72,88, …实际上在分配内存是调用的是 C 语言的 realloc 方法。
- insert
调用逻辑:listinsert-> ins1->list_resize
+1 ,移动元素后插入新数据。
Objects/listobject.c
|
|
其他方法这里就不一一列举了。
4. Python 中字典的实现
Python 中的字典是通过哈希表实现的。哈希表是一个数组,它的索引是对键运用哈希函数求得的,采用开放地址法解决冲突问题。
- 定义
Include/dictobject.h
|
|
上面是 字典 Key 值状态转换图。如果删除一个 key,这个元素将被设置为 dummy,并不是真的删除。dummy 状态的元素在插入新的元素之后,变为激活状态。
- 基本函数
Objects/dictobject.c
|
|
- 初始化
Objects/dictobject.c
|
|
这里说明一下,字典对象的缓冲池free_list
, 是一个长度为 80 的数组。
- get
调用逻辑:dict_get
-> lookdict_string
-> lookdict
,返回查询到的值
Objects/dictobject.c
|
|
|
|