86a588bff94707de3aa6b6f6cbd7991a
Python中的元组对象

Python中的元组对象

终于即将进入到Python对象的最后一个章节了。我们来谈谈Python中一个比较有意思的对象,元组。

很多编程语言里面都没有默认提供元组的实现,因此我们常常需要利用数组或者结构体甚至是对象引用(指针)来满足我们对于多返回值等一些场景的需求。

Python中默认提供了元组对象,让我们能够写出如下代码:

k = (1, 2, 3, 4, 5, 6)

今天就让我们一起来探讨下它的实现过程吧。

PyTupleObject

[tupleobject.h]中,我们不难发现元组对象的数据结构。毫无疑问,为了支持任意个数的元组,他必然是个变长对象,如下所示:

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;

看过我之前文章的朋友可能乍一看觉得,PyTupleObject怎么这么像PyListObject。没错,下文我们会看到她们之间的相似之处。但在这,我们可以明确指出,PyTupleObject是个创建后容纳对象长度不会变的结构,ob_size并不会有扩充或者收缩。

当然,我说的是表象上的现象。

元组对象的创建

[tupleobject.c]中,我们可以看出,PyTuple_New承担了创建元组对象及分配内存的任务,如下所示:

PyObject *
PyTuple_New(register Py_ssize_t size)
{
    register PyTupleObject *op;
    Py_ssize_t i;
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
#if PyTuple_MAXSAVESIZE > 0
    if (size == 0 && free_list[0]) {
        op = free_list[0];
        Py_INCREF(op);
        return (PyObject *) op;
    }
    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);
    }
    else
#endif
    {
        Py_ssize_t nbytes = size * sizeof(PyObject *);
        /* Check for overflow */
        if (nbytes / sizeof(PyObject *) != (size_t)size ||
            (nbytes > PY_SSIZE_T_MAX - sizeof(PyTupleObject) - 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
    if (size == 0) {
        free_list[0] = op;
        ++numfree[0];
        Py_INCREF(op);          /* extra INCREF so that this is never freed */
    }
#endif
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}
  • 创建的时候,元组就知道自身的大小

  • 根据传入的大小,ob_item分配为对应大小的数组。

  • 对于大小为0的元组,还是采用引用计数公用的方式,避免多次创建浪费,如下所示:

    if (size == 0 && free_list[0]) {
top Created with Sketch.