与列表切片相反,元组切片不返回新对象

Vij*_*gir 12 python tuples cpython list slice

在Python(2和3)中。每当我们使用列表切片时,它都会返回一个新对象,例如:

l1 = [1,2,3,4]
print(id(l1))
l2 = l1[:]
print(id(l2))
Run Code Online (Sandbox Code Playgroud)

输出量

>>> 140344378384464
>>> 140344378387272
Run Code Online (Sandbox Code Playgroud)

如果使用元组重复相同的事情,则返回相同的对象,例如:

t1 = (1,2,3,4)
t2 = t1[:]
print(id(t1))
print(id(t2))
Run Code Online (Sandbox Code Playgroud)

输出量

>>> 140344379214896
>>> 140344379214896
Run Code Online (Sandbox Code Playgroud)

如果有人能弄清为什么会发生,那将是很棒的,在我的整个Python经验中,我一直以为空切片会返回一个新对象的印象。

我的理解是,它返回的对象与元组是不可变的相同,因此没有必要为其创建新副本。但是同样,文档中也没有提到它。

wim*_*wim 13

实现可以自由地为不变类型返回相同的实例(在CPython中,有时可能会看到类似的针对字符串和整数的优化)。由于无法更改对象,因此用户代码中没有任何内容需要关心它是保存唯一实例还是仅是对现有实例的另一个引用。

您可以在此处的C代码中找到短路。

static PyObject*
tuplesubscript(PyTupleObject* self, PyObject* item)
{
    ... /* note: irrelevant parts snipped out */
    if (start == 0 && step == 1 &&
                 slicelength == PyTuple_GET_SIZE(self) &&
                 PyTuple_CheckExact(self)) {
            Py_INCREF(self);          /* <--- increase reference count */
            return (PyObject *)self;  /* <--- return another pointer to same */
        }
    ...
Run Code Online (Sandbox Code Playgroud)

这是一个实现细节,请注意pypy不会做同样的事情。

  • 查看元组的typedef可能会有所帮助,请在此处(https://github.com/python/cpython/blob/3.8/Include/cpython/tupleobject.h#L14)。所以`a-&gt; ob_item`就像`(* a).ob_item`,也就是说,它从a指向的PyTupleObject`中获得名为ob_item的成员,然后+ ilow前进到切片的开头。 (2认同)