我们已经学习了 list 对象的内部结构,知道它底层是用动态数组实现的。在 list 头部进行插入或删除,都要挪动其后的所有数据,性能非常差!因此,我们不能将 list 对象作为队列使用。
好在 Python 标准库提供了另一种对象—— deque ,很好地补全了 list 的短板。deque 是一种类似 list 的线性表,但它在两端插入删除数据的时间复杂度都是 ,因而可以作为队列来使用。
from collections import deque
# 创建一个deque对象作为队列
q = deque()
# 数据入队:将其插到末尾
q.append(data)
# 数据出队:从头部弹出
data = q.popleft()
您可能会问了,deque 到底跟 list 有何不同?为何两端都能同时支持 的插入删除呢?
内部结构
这其中的秘密还得到源码中寻找。Python标准库位于源码中的 Lib 目录,collections 模块源码位于 Lib/collections 目录,模块入口文件为 Lib/collections/__init__.py 。打开 __init__.py 可以看到这段代码( 29~34 行):
try:
from _collections import deque
except ImportError:
pass
else:
_collections_abc.MutableSequence.register(deque)
由此可见,deque 是在另一个模块 _collections 中实现的。我们在 Lib 中并没有找到 _collections模块,它很有可能是一个用 C 语言实现的模块。滋补小铺我们回到 Modules 目录看一看,果然找到了 Modules/_collectionsmodule.c 。
这个源码文件就是 deque 实现之所在,不难找到 deque 对象底层结构体定义( 71~96 行):
typedef struct BLOCK {
struct BLOCK *leftlink;
PyObject *data[BLOCKLEN];
struct BLOCK *rightlink;
} block;
typedef struct {
PyObject_VAR_HEAD
block *leftblock;
block *rightblock;
Py_ssize_t leftindex; /* 0 <= leftindex < BLOCKLEN */
Py_ssize_t rightindex; /* 0 <= rightindex < BLOCKLEN */
size_t state; /* incremented whenever the indices move */
Py_ssize_t maxlen; /* maxlen is -1 for unbounded deques */
PyObject *weakreflist;
} dequeobject;
这里的源码以 Python 3.7.4 版本为例,不同版本可能略有差异。
dequeobject 结构体便是 deque 实例对象的底层肉身,可以看到 deque 将自己保存的元素组织成链表结构,难怪在两端增删元素都很快!跟普通链表节点只保存一个元素不同,deque 的链表节点可以保存很多元素。
block 结构体就是 deque 内部的链表节点,顾名思义,它是一个内存块。block 结构体非常简单,只有 3 个字段:
-
leftlink ,左向指针,指向左边(更接近头部)的内存块;
-
rightlink ,右向指针,指向右边(更接近尾部)的内存块;
-
data ,对象指针数组,deque 中的元素就保存在这里;
内存块 block 由 leftlink 和 rightlink 字段串联,组成一个双向链表。block 内存块中的 data 数组,长度由 BLOCKLEN 宏定义决定( 第 21 行 ):
#define