Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tuple slicing not returning a new object as opposed to list slicing

In Python (2 and 3). Whenever we use list slicing it returns a new object, e.g.:

l1 = [1,2,3,4]
print(id(l1))
l2 = l1[:]
print(id(l2))

Output

>>> 140344378384464
>>> 140344378387272

If the same thing is repeated with tuple, the same object is returned, e.g.:

t1 = (1,2,3,4)
t2 = t1[:]
print(id(t1))
print(id(t2))

Output

>>> 140344379214896
>>> 140344379214896

It would be great if someone can shed some light on why this is happening, throughout my Python experience I was under the impression empty slice returns a new object.

My understanding is that it's returning the same object as tuples are immutable and there's no point of creating a new copy of it. But again, it's not mentioned in the documents anywhere.

like image 950
Vijay Jangir Avatar asked Oct 22 '19 15:10

Vijay Jangir


People also ask

Does slicing a list create a new object?

Slicing lists does not generate copies of the objects in the list; it just copies the references to them. That is the answer to the question as asked.

Can a tuple be sliced in the same way as a list?

We can use slicing in tuples I'm the same way as we use in strings and lists. Tuple slicing is basically used to obtain a range of items.

Does tuple slicing create a new tuple?

The slicing operation on a Python tuple creates another tuple, as specified by the two indexes within the square brackets. The returned tuple contains the shallow copy of the elements from the original tuple.

Does tuple support slicing in Python?

Slicing. We can access a range of items in a tuple by using the slicing operator colon : . Slicing can be best visualized by considering the index to be between the elements as shown below. So if we want to access a range, we need the index that will slice the portion from the tuple.


2 Answers

Implementations are free to return identical instances for immutable types (in CPython, you may sometimes see similar optimizations for strings and integers). Since the object can not be changed, there is nothing in user code that needs to care whether it holds a unique instance or just another reference to an existing instance.

You can find the short-circuit in the C code here.

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 */
        }
    ...

This is an implementation detail, note that pypy does not do the same.

like image 182
wim Avatar answered Sep 27 '22 21:09

wim


It's an implementation detail. Because lists are mutable, l1[:] must create a copy, because you wouldn't expect changes to l2 to affect l1.

Since a tuple is immutable, though, there's nothing you can do to t2 that would affect t1 in any visible way, so the compiler is free (but not required) to use the same object for t1 and t1[:].

like image 24
chepner Avatar answered Sep 27 '22 22:09

chepner