深入理解 Python 虚拟机:元组(tuple)的实现原理及源码剖析
元组的结构
在这一小节当中主要介绍在 python 当中元组的数据结构:
typedef struct {
PyObject_VAR_HEAD
PyObject *ob_item[1];
/* ob_item contains space for 'ob_size' elements.
* Items must normally not be NULL, except during construction when
* the tuple is not yet visible outside the function that builds it.
*/
} PyTupleObject;
#define PyObject_VAR_HEAD PyVarObject ob_base;
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
从上面的数据结构来看和 list 的数据结构基本上差不多,最终的使用方法也差不多。将上面的结构体展开之后,PyTupleObject 的结构大致如下所示:
-
ob_type,表示这个对象的数据类型是什么,在 python 当中有时候需要对数据的数据类型进行判断比如 isinstance, type 这两个关键字就会使用到这个字段。
-
ob_item,这是一个指针,指向真正保存 python 对象数据的地址,大致的内存他们之间大致的内存布局如下所示:
Py_ssize_t,一个整型数据类型。
元组操作函数源码剖析
创建元组
首先我们需要了解一下在 cpython 内部关于元组内存分配的问题,首先和 list 一样,在 cpython 当中对于分配的好的元组进行释放的时候,并不会直接进行释放,而是会先保存下来,当下次又有元组申请内存的时候,直接将这块内存进行返回即可。
static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
- free_list,保存指针——指向被释放的元组。
- numfree,对应的下标表示元组当中元素的个数,numfree[i] 表示有 i 个元素的元组的个数。
下面是新建 tuple 对象的源程序:
PyObject *
PyTuple_New(Py_ssize_t size
{
PyTupleObject *op;
Py_ssize_t i;
if (size < 0 {
PyErr_BadInternalCall(;
return NULL;
}
#if PyTuple_MAXSAVESIZE > 0
// 如果申请一个空的元组对象 当前的 free_list 当中是否存在空元组对象 如果存在则直接返回
if (size == 0 && free_list[0] k
op = free_list[0];
Py_INCREF(op;
return (PyObject * op;
}
// 如果元组的对象元素个数小于 20 而且对应的 free_list 当中还有余下的元组对象 则不需要进行内存申请直接返回
if (size < PyTuple_MAXSAVESIZE && (op = free_list[size] != NULL {
free_list[size] = (PyTupleObject * op->ob_item[0];
numfree[size]--;
/* Inline PyObject_InitVar */
_Py_NewReference((PyObject *op; // _Py_NewReference 这个宏是将对象 op 的引用计数设置成 1
}
else
#endif
{
/* Check for overflow */
// 如果元组的元素个数大或者等于 20 或者 当前 free_list 当中没有没有剩余的对象则需要进行内存申请
if ((size_tsize > ((size_tPY_SSIZE_T_MAX - sizeof(PyTupleObject -
sizeof(PyObject * / sizeof(PyObject * {
// 如果元组长度大于某个值直接报内存错误
return PyErr_NoMemory(;
}
// 申请元组大小的内存空间
op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size;
if (op == NULL
return NULL;
}
// 初始化内存空间
for (i=0; i < size; i++
op->ob_item[i] = NULL;
#if PyTuple_MAXSAVESIZE > 0
// 因为 size == 0 的元组不会进行修改操作 因此可以直接将这个申请到的对象放到 free_list 当中以备后续使用
if (size == 0 {
free_list[0] = op;
++numfree[0];
Py_INCREF(op;