python 元组对象 底层实现 源码分析 PyTupleObject(tuple)

PyTupleObject

本文参考的是 3.8.0a0 版本的代码,详见  cpython 源码分析 基本篇

以后都在 github 更新,请参考 图解python tuple

python 的内建对象除了 list 这个可变的数据结构,还有 tuple 这个不可变的数据结构,不可变的意思是这个 tuple 的长度在创建的时候就是恒定的,不能增加/删除元素,但是 tuple 里面存储的对象却可以是可变的

他的 memory layout 如下所示

python 元组对象 底层实现 源码分析 PyTupleObject(tuple)_第1张图片

 

我们可以看到,PyTupleObject 只存储了两个对象,分别是:

PyObject_VAR_HEAD: cpython中容器对象的头部

ob_item: 一个长度为 1 的存储内容为 PyObject * 的数组

 

free_list

和 dict 还有 list 一样,为了提高效率,避免频繁的调用系统函数 free 和 malloc 向操作系统申请和释放空间,在文件:

Objects/tupleobject.c 中第 28 行定义了一个 free_list: 

static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];

所有申请过的,小于一定大小的 tuple,在释放的时候会被放进这个 free_list 中以供下次使用,如下图所示

python 元组对象 底层实现 源码分析 PyTupleObject(tuple)_第2张图片

free_list[0] 用于存储长度为 0 的 tuple 对象,整个解释器的生命周期里面只有一个长度为 0 的 tuple 对象实例

free_list[1] 用于存储长度为 1 的 tuple 对象,可以通过 tuple 对象的 ob_item[0] 指针遍历到下一个长度为 1 的 tuple 对象

free_list[2] 用于存储长度为 2 的 tuple 对象,上图画的 free_list[2] 中只有一个对象

free_list 每一格最多能存储 PyTuple_MAXFREELIST 个 tuple 链表长度,这里定义的是 2000

 

我们来看下 PyTuple_New 函数

/* Objects/tupleobject.c 79 - 136 行 */

PyObject *
PyTuple_New(Py_ssize_t size)
{
    PyTupleObject *op;
    Py_ssize_t i;
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
/* 如果启动了 free_list 存储 */
#if PyTuple_MAXSAVESIZE > 0
    if (size == 0 && free_list[0]) {
        /* 如果 size 为 0, 则返回 free_list 的第一个元素 */
        op = free_list[0];
        Py_INCREF(op);
#ifdef COUNT_ALLOCS
        _Py_tuple_zero_allocs++;
#endif
        return (PyObject *) op;
    }
    if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
        /* 如果 size 在 free_list 范围内,并且 free_list[size] 存有之前回收过的对应size的tuple 对象 
        把 free_list[size] 指向 op 的下一个链表地址
        */
        free_list[size] = (PyTupleObject *) op->ob_item[0];
        numfree[size]--;
#ifdef COUNT_ALLOCS
        _Py_fast_tuple_allocs++;
#endif
        /* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
        Py_SIZE(op) = size;
        Py_TYPE(op) = &PyTuple_Type;
#endif
        _Py_NewReference((PyObject *)op);
    }
    else
#endif
    {
        /* free_list 未找到对应大小的 tuple 并且 size 不为 0,向操作系统分配 */
        /* Check for overflow */
        if ((size_t)size > ((size_t)PY_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;
    }
    /* 把 tuple 里面的元素设置为空指针 */
    for (i=0; i < size; i++)
        op->ob_item[i] = NULL;
#if PyTuple_MAXSAVESIZE > 0
    if (size == 0) {
        free_list[0] = op;
        ++numfree[0];
        Py_INCREF(op);          /* extra INCREF so that this is never freed */
    }
#endif
#ifdef SHOW_TRACK_COUNT
    count_tracked++;
#endif
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}

其他的函数看名称也大概清楚,就不详细说了 

参考资料:

How is tuple implemented in CPython?

你可能感兴趣的:(python,python,internal,python,python3,源码,cpython,c)