By Akaisteph7


2019-02-10 05:52:38 8 Comments

So I just came across what seems to me like a strange Python feature and wanted some clarification about it.

The following array manipulation somewhat makes sense:

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]

I imagine it is actually just appending this value to the end, correct?
Why can I do this, however?

p[20:22] = [5,6]
p = [1,2,3,4,5,6]

And even more so this:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]

This just seems like wrong logic. It seems like this should throw an error!

Any explanation?
-Is it just a weird thing Python does?
-Is there a purpose to it?
-Or am I thinking about this the wrong way?

2 comments

@Raymond Hettinger 2019-02-10 06:16:45

Part of question regarding out-of-range indices

Slice logic automatically clips the indices to the length of the sequence.

Allowing slice indices to extend past end points was done for convenience. It would be a pain to have to range check every expression and then adjust the limits manually, so Python does it for you.

Consider the use case of wanting to display no more than the first 50 characters of a text message.

The easy way (what Python does now):

preview = msg[:50]

Or the hard way (do the limit checks yourself):

n = len(msg)
preview = msg[:50] if n > 50 else msg

Manually implementing that logic for adjustment of end points would be easy to forget, would be easy to get wrong (updating the 50 in two places), would be wordy, and would be slow. Python moves that logic to its internals where it is succint, automatic, fast, and correct. This is one of the reasons I love Python :-)

Part of question regarding assignments length mismatch from input length

The OP also wanted to know the rationale for allowing assignments such as p[20:100] = [7,8] where the assignment target has a different length (80) than the replacement data length (2).

It's easiest to see the motivation by an analogy with strings. Consider, "five little monkeys".replace("little", "humongous"). Note that the target "little" has only six letters and "humongous" has nine. We can do the same with lists:

>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'

This all comes down to convenience.

Prior to the introduction of the copy() and clear() methods, these used to be popular idioms:

s[:] = []           # clear a list
t = u[:]            # copy a list

Even now, we use this to update lists when filtering:

s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values

Hope these practical examples give a good perspective on why slicing works as it does.

@Quuxplusone 2019-02-10 21:44:56

"Even now, we use this to update lists when filtering [example using s[:]]" — Could you expand on why you'd use s[:] = there, instead of just s =? I've never seen anyone use s[:] = in the context of a line such as what you wrote there. Good answer otherwise!

@Daniel Pryden 2019-02-11 00:29:36

@Quuxplusone: Slice assignment mutates the list already referenced by s; using s = re-binds s to refer to a new list. If the list can be reached via multiple names, and you want the mutation to be visible to all the names, slice assignment is what you want. Also, if s were global, reassigning s would require a global declaration, but slice assignment would have a similar effect even without the global statement.

@iz_ 2019-02-10 06:09:28

The documentation has your answer:

s[i:j]: slice of s from i to j (note (4))

(4) The slice of s from i to j is defined as the sequence of items with index k such that i <= k < j. If i or j is greater than len(s), use len(s). If i is omitted or None, use 0. If j is omitted or None, use len(s). If i is greater than or equal to j, the slice is empty.

The documentation of IndexError confirms this behavior:

exception IndexError

Raised when a sequence subscript is out of range. (Slice indices are silently truncated to fall in the allowed range; if an index is not an integer, TypeError is raised.)

Essentially, stuff like p[20:100] is being reduced to p[len(p):len(p]. p[len(p):len(p] is an empty slice at the end of the list, and assigning a list to it will modify the end of the list to contain said list. Thus, it works like appending/extending the original list.

This behavior is the same as what happens when you assign a list to an empty slice anywhere in the original list. For example:

In [1]: p = [1, 2, 3, 4]

In [2]: p[2:2] = [42, 42, 42]

In [3]: p
Out[3]: [1, 2, 42, 42, 42, 3, 4]

@Primusa 2019-02-10 06:10:57

I don't think OP is asking how slicing works, he's asking for the rationale behind the design choice.

@g.d.d.c 2019-02-10 06:14:57

@Primusa - I believe they're asking both. This explains the how, which is good to know because it explains why the behavior isn't broken. The why is probably buried in the depths of one of the mailing lists somewhere.

@Atirag 2019-02-10 06:15:08

Good answer but this doesn't explain why the new numbers get appended to the end of the list.

@iz_ 2019-02-10 06:22:40

@Atirag I added a small blurb about it for completeness.

@Atirag 2019-02-10 06:29:38

It is a bit confusing though that p[len(p):len(p)] is empty but p[len(p)] is out of range. Following the logic from the former I would assume p[len(p)] =[c,d] would also append the values but it won't of course.

@iz_ 2019-02-10 06:30:31

@Atirag Indexing is very different from slicing; indexing always refers to values.

@Atirag 2019-02-10 06:31:37

Yeah I guess the definition is clear about it just the syntax is confusing.

@Akaisteph7 2019-02-10 08:00:01

Thank you for the documentation. It made it easier to understand the how.

@iz_ 2019-04-05 23:13:32

@Programmer What else needs to be explained? Please add some more details as to what you think is wrong.

Related Questions

Sponsored Content

19 Answered Questions

[SOLVED] How to remove an element from a list by index?

  • 2009-03-09 18:16:11
  • Joan Venge
  • 2276014 View
  • 1280 Score
  • 19 Answer
  • Tags:   python list

21 Answered Questions

[SOLVED] Does Python have a ternary conditional operator?

28 Answered Questions

[SOLVED] Finding the index of an item given a list containing it in Python

  • 2008-10-07 01:39:38
  • Eugene M
  • 3369243 View
  • 2783 Score
  • 28 Answer
  • Tags:   python list indexing

10 Answered Questions

10 Answered Questions

[SOLVED] Does Python have a string 'contains' substring method?

9 Answered Questions

14 Answered Questions

9 Answered Questions

[SOLVED] Python join: why is it string.join(list) instead of list.join(string)?

  • 2009-01-29 22:45:13
  • Evan Fosmark
  • 1217046 View
  • 1619 Score
  • 9 Answer
  • Tags:   python string list join

26 Answered Questions

[SOLVED] Why are Python lambdas useful?

3 Answered Questions

[SOLVED] Why does Python code run faster in a function?

Sponsored Content