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.
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.
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.
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.
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.
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.
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[:]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With