Why Does Substring Slicing With Index Out of Range Work

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

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.

Python index out of range in list slicing

When you are accessing an element in a list whose index is beyond its length, we cannot return anything. (There is no way we can represent an element which is not there). That's why the error is thrown. But when you are slicing, you are making a sliced COPY of the original list and that new list can be empty if the start or end are not valid.

Why python's list slicing doesn't produce index out of bound error?

Slicing is used to create a new list. If the indices don't fall within the range of the number of elements in the list, we can return an empty list. So, we don't have to throw an error.

But, if we try to access the elements in the list which is greater than the number of elements, we cannot return any default value (not even None because it could be a valid value in the list). That is why

IndexError: list index out of range

is thrown.

While slicing, if the starting index is greater than or equal to the length of the sequence, the length of the returned sequence is set to be 0, in this line

defstop = *step < 0 ? -1 : length;
...
if (r->stop == Py_None) {
*stop = defstop;
}
...
if ((*step < 0 && *stop >= *start)
|| (*step > 0 && *start >= *stop)) {
*slicelength = 0;

For the Strings, if the length of the string to be returned after slicing is 0, then it returns an empty string, in this line

if (slicelength <= 0) {
return PyString_FromStringAndSize("", 0);
}

Python String index out of range, difference between s[0] and s[:1]

This is because Python is tolerant towards out-of-bound indices for slices, while intolerant towards out-of-bound indices for lists/strings themselves, as demonstrated below:

>>> ''[:1] # works even though the string does not have an index 0
''
>>> ''[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range

index out of range when working with slices

type Label struct {
Value string `json:"value"`
}

type AnnotV2 struct {
Uuid string `json:"uuid"`
Annotation struct {
Classif struct {
Niveau1 struct {
Labels []Label `json:"labels"`
}
Niveau2 struct {
Labels []Label `json:"labels"`
}
Niveau3 struct {
Labels []Label `json:"labels"`
}
} `json:"classifications"`
} `json:"annotation"`
User string `json:"user"`
}

pre-allocate the slice

if element.Data.Niveau2 != nil {
newAnnot.Annotation.Classif.Niveau2.Labels = make([]Label, len(element.Data.Niveau2))
for j, annot2 := range element.Data.Niveau2 {
newAnnot.Annotation.Classif.Niveau2.Labels[j].Value = annot2
}
}

or use append

if element.Data.Niveau2 != nil {
for _, annot2 := range element.Data.Niveau2 {
newAnnot.Annotation.Classif.Niveau2.Labels = append(newAnnot.Annotation.Classif.Niveau2.Labels, Label{annot2})
}
}


Related Topics



Leave a reply



Submit