135

Python docs says that slicing a list returns a new list.
Now if a "new" list is being returned I've the following questions related to "Assignment to slices"

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

Now the output would be:

[4, 5, 3] 
  1. How can something that is returning something come on the left side of expression?
  2. Yes, I read the docs and it says it is possible, now since slicing a list returns a "new" list, why is the original list being modified? I am not able to understand the mechanics behind it.
martineau
  • 119,623
  • 25
  • 170
  • 301
Kartik Anand
  • 4,513
  • 5
  • 41
  • 72
  • @Mark Longair sorry I thought only code is supposed to be formatted not the output – Kartik Anand May 16 '12 at 17:07
  • 2
    See: [6.2 Assignment statements](http://docs.python.org/reference/simple_stmts.html#assignment-statements) – Josh Lee May 16 '12 at 17:10
  • yes i do understand assignments,but this concept of a new list being generated on slicing, getting on my nerves – Kartik Anand May 16 '12 at 17:15
  • 1
    @KartikAnand Slice assignment is a special scenario where a new list is not created. It doesn't make sense to create an object without a name binding on the left side of an `=`, so instead of discarding this as invalid syntax, python turns it into something more like what you might expect. Since python does not have references, it would not work to have the result of a slice change the original list. You get a copy. If you provided more info on your application, we might be able to better help you do things in the 'pythonic' way. :) – Casey Kuball May 16 '12 at 23:21
  • 1
    @Darthfett I am not working on any application right now rather I am teaching myself python before I start getting my hands dirty :) – Kartik Anand May 17 '12 at 03:36

5 Answers5

155

You are confusing two distinct operation that use very similar syntax:

1) slicing:

b = a[0:2]

This makes a copy of the slice of a and assigns it to b.

2) slice assignment:

a[0:2] = b

This replaces the slice of a with the contents of b.

Although the syntax is similar (I imagine by design!), these are two different operations.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 8
    That's what my doubt is,in the second case,why isn't the slice of a, a new list?? – Kartik Anand May 16 '12 at 17:12
  • 16
    @KartikAnand Because it isn't. That's not what the language specifies. – Marcin May 16 '12 at 17:12
  • To be clear, "takes a slice of" really means "make a copy of a slice of" which is where part of the confusion comes from. – Mark Ransom May 16 '12 at 17:12
  • so the difference is because of which side I am using slice(left or right;assignment operator)..and using it on the left side is in effect not returning anything. – Kartik Anand May 16 '12 at 17:19
  • 3
    @KartikAnand: Basically, yes. The interpreter knows which is which, and handles them appropriately. – NPE May 16 '12 at 17:21
  • @aix so can I say that using slice on the left side is effectively replacing slice with individual list elements..as in..a[1:3] => a[1], a[2] – Kartik Anand May 16 '12 at 17:23
  • Is there any way to refer to a slice of a list (i.e. slicing, not slice assignment) without creating a whole new copy of the list in memory? If I have a (very) long list, and want to loop over all but the first element, my instinct is `for i in list[1:]:`, but I don't want any duplication of the memory. How can I do that? – Dubslow Nov 09 '12 at 07:16
  • It's comparable to subscript syntax (per the BNF): b = a[2] vs. a[2] = b – user117529 May 02 '13 at 21:45
  • 2
    @Dubslow: you can do that by using the [itertools](https://docs.python.org/2/library/itertools.html) module. For your case use the function [islice](https://docs.python.org/2/library/itertools.html#itertools.islice), with `start=1`, `stop=None`. This will avoid any copies and use lazy evaluation (in your case lazy access ot the original list). – Spiros Jun 25 '15 at 09:27
91

When you specify a on the left side of the = operator, you are using Python's normal assignment, which changes the name a in the current context to point to the new value. This does not change the previous value to which a was pointing.

By specifying a[0:2] on the left side of the = operator, you are telling Python you want to use slice assignment. Slice assignment is a special syntax for lists, where you can insert, delete, or replace contents from a list:

Insertion:

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

Deletion:

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

Replacement:

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

Note:

The length of the slice may be different from the length of the assigned sequence, thus changing the length of the target sequence, if the target sequence allows it. - source

Slice assignment provides similar function to tuple unpacking. For example, a[0:1] = [4, 5] is equivalent to:

# Tuple Unpacking
a[0], a[1] = [4, 5]

With tuple unpacking, you can modify non-sequential lists:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

However, tuple unpacking is limited to replacement, as you cannot insert or remove elements.

Before and after all these operations, a is the same exact list. Python simply provides nice syntactic sugar to modify a list in-place.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Casey Kuball
  • 7,717
  • 5
  • 38
  • 70
  • 7
    Similar but not identical, since you can have unequal numbers of elements on the left and right. – Mark Ransom May 16 '12 at 17:14
  • @MarkRansom That's an excellent point, I have added more info to make this obvious. – Casey Kuball May 16 '12 at 17:26
  • 2
    Is `a[:] = some_list` equivalent to `a = some_list[:]` or `a = some_list`? – jadkik94 May 17 '12 at 10:42
  • 2
    @jadkik94 Neither. `a[:] = some_list` sets every element of `a` to be those of `some_list`. Doing either of the ones you mention would change what `a` is. For example: `a = [1, 2, 3]` `b = a` `a[:] = [4, 5, 6]` `a is b`. The last line would be False if it changed `a`'s value, rather than mutating it. – Casey Kuball May 17 '12 at 13:16
  • @CaseyKuball Does the value of `start: stop: step` should always be positive when we are doing slice assignment? – strikersps Sep 20 '20 at 15:33
  • @strikersps, I've never encountered any such limitation. You can use negative indices just as well as positive for referencing the end of a list. However, there is a limitation in that you can't "wrap" from the end to the beginning. Experimenting, `a[-5:3] = list(range(8))` seems to insert the list of values at `a[-5] `, rather than replacing `a[-5:]` and `a[:3]`. However, you can use negative start and stop together, as well as a negative step. – Casey Kuball Sep 20 '20 at 22:31
37

I came across the same question before and it's related to the language specification. According to assignment-statements,

  1. If the left side of assignment is subscription, Python will call __setitem__ on that object. a[i] = x is equivalent to a.__setitem__(i, x).

  2. If the left side of assignment is slice, Python will also call __setitem__, but with different arguments: a[1:4]=[1,2,3] is equivalent to a.__setitem__(slice(1,4,None), [1,2,3])

That's why list slice on the left side of '=' behaves differently.

Dan D.
  • 73,243
  • 15
  • 104
  • 123
Stan
  • 2,010
  • 3
  • 16
  • 12
5

By slicing on the left hand side of an assignment operation, you are specifying which items to assign to.

fraxel
  • 34,470
  • 11
  • 98
  • 102
2

When you did a[0:2] = [4,5], you assigned the the left hand side of the = (the slice a[0:2]) the value on the right side of the =, [4,5].

thing10
  • 140
  • 1
  • 9