Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between '[:]' and '[::]' slicing when copying a list?

We can (shallow) copy a list by using [:]:

l = [1, 2, 3]
z1 = l[:]

We can also (shallow) copy it by using [::]:

z2 = l[::]

and now z1 == z2 will be True. I understand how these slices work after reading the answers in Explain Python's slice notation.

But, my question is, is there any difference between these two internally? Is one more efficient than the other in copying or do they do exactly the same things?

like image 729
user6774416 Avatar asked Jan 14 '17 10:01

user6774416


People also ask

Does slicing copy a list?

Use slicing List slicing can be used to easily make a copy of a list. This method is called cloning. The original list will remain unchanged. In this method, slicing is used to copy each element of the original list into the new list.

What is the use of slicing in list?

List slicing returns a new list from the existing list. If Lst is a list, then the above expression returns the portion of the list from index Initial to index End, at a step size IndexJump.

How do I copy a list using slicing?

First: Copying by SlicingWhen you omit the start index and the end index from the slice, then your slice will start from the beginning of the list all the way to the end of the list. And because slicing creates a new object, then the above code effectively copies or clones the whole list into another list.

What does it mean to slice a list in Python?

In short, slicing is a flexible tool to build new lists out of an existing list. Python supports slice notation for any sequential data type like lists, strings, tuples, bytes, bytearrays, and ranges. Also, any new data structure can add its support as well.


2 Answers

Absolutely no difference between them, at least in Python 3. You can check the byte-code produced for each of these using dis.dis if you'd like:

l = [1, 2, 3, 4]

Byte-code emitted for l[:]:

from dis import dis
dis('l[:]')
  1           0 LOAD_NAME                0 (l)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 BUILD_SLICE              2
             12 BINARY_SUBSCR
             13 RETURN_VALUE

while, byte-code emitted for l[::]:

dis('l[::]')
  1           0 LOAD_NAME                0 (l)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 BUILD_SLICE              2
             12 BINARY_SUBSCR
             13 RETURN_VALUE

as you can see, they're exactly the same. Both load some None's (the two LOAD_CONSTS's) for the values of start and stop used for building the slice (BUILD_SLICE) and apply it. Nones are the default for these as stated in the docs for slices in the Standard Type hierarchy:

Special read-only attributes: start is the lower bound; stop is the upper bound; step is the step value; each is None if omitted. These attributes can have any type.

Use [:], it's less key-strokes.


It's actually interesting to note that in Python 2.x the byte code generated is different and, due to less commands for l[:] it might be slightly more performant:

>>> def foo():
...     l[:]
... 
>>> dis(foo)
  2           0 LOAD_GLOBAL              0 (l)
              3 SLICE+0             
              4 POP_TOP             
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE 

While, for l[::]:

>>> def foo2():
...     l[::]
... 
>>> dis(foo2)
  2           0 LOAD_GLOBAL              0 (l)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 LOAD_CONST               0 (None)
             12 BUILD_SLICE              3
             15 BINARY_SUBSCR       
             16 POP_TOP             
             17 LOAD_CONST               0 (None)
             20 RETURN_VALUE 

Even though I haven't timed these (and I won't, the difference should be tiny) it seems that, due to simply less instructions needed, l[:] might be slightly better.


This similarity doesn't of course exist only for lists; it applies to all Sequences in Python:

# Note: the Bytecode class exists in Py > 3.4
>>> from dis import Bytecode
>>>
>>> Bytecode('(1, 2, 3)[:]').dis() == Bytecode('(1, 2, 3)[::]').dis() 
True
>>> Bytecode('"string"[:]').dis() == Bytecode('"string"[::]').dis() 
True

similarly for others.

like image 131
Dimitris Fasarakis Hilliard Avatar answered Oct 11 '22 10:10

Dimitris Fasarakis Hilliard


Per the Python language reference section 6.3.2, Subscriptions, the inner expression for a sequence must evaluate to either an integer or a slice. Both these examples produce the same slice, and are therefore identical. There are also numerous other slices that have the same effect, by explicitly stating defaults (start=0, stop=len(sequence) or more, step=1).

like image 42
Yann Vernier Avatar answered Oct 11 '22 11:10

Yann Vernier