Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Python allow out-of-range slice indexes for sequences?

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?

like image 409
Akaisteph7 Avatar asked Feb 10 '19 05:02

Akaisteph7


People also ask

What happen if you use out of range index with slicing?

The slicing operation doesn't raise an error if both your start and stop indices are larger than the sequence length. This is in contrast to simple indexing—if you index an element that is out of bounds, Python will throw an index out of bounds error. However, with slicing it simply returns an empty sequence.

Can we use slicing in range in Python?

Slicing range() function in Python Depending on how many arguments the user is passing to the function, the user can decide where that series of numbers will begin and end as well as how big the difference will be between one number and the next.

What is sequence slicing in Python?

Slicing in Python is a feature that enables accessing parts of sequences like strings, tuples, and lists. You can also use them to modify or delete the items of mutable sequences such as lists. Slices can also be applied on third-party objects like NumPy arrays, as well as Pandas series and data frames.

What is the difference between slicing and range in Python?

List slicing uses [:] and Range uses (,) One is using Square Brackets with colon, One is using Brackets with comma. Can anyone help me to understand the two, and what should I be aware of when I deal with these type? Maybe an example? thank you so much!


2 Answers

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.

like image 121
Raymond Hettinger Avatar answered Sep 24 '22 00:09

Raymond Hettinger


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] 
like image 22
iz_ Avatar answered Sep 23 '22 00:09

iz_