0

So, I'm self teaching programming for the first time and I decided to start with Python! I'm learning about strings and lists. I know strings are immutable and lists are mutable. My doubt comes from the fact that:

>>> s = "hello"
>>> s[5]

returns "IndexError: string index out of range" OK I get it cause strings as I said are immutable and s only has 5 elements (from index 0 to 4).

But if I issue:

>>> s[0:1000]

I get:

'hello'

I understand that if I issue the same command on a list it will return every element because lists are mutable and I can re-assign values but How come no errors appear with strings? Indexes 5 to 1000 don't exist.

Thank you!!!

totito88
  • 19
  • 4
  • what does mutability have to do with it regarding lists? – juanpa.arrivillaga Oct 17 '19 at 17:29
  • 2
    Anyway, the question of *why* is answered by "because the language designers decided for it to work that way". it *could* have thrown an error, but this behavior was chosen – juanpa.arrivillaga Oct 17 '19 at 17:30
  • Slices obey a nice law that says `s[x:y] + s[y:z] == s[x:z]` for all `x <= y <= z`, and because there's never a need to conjure items out of thin air for that to be true, there's no reason to limit this to `0 <= x <= y <= z < len(s)`. – chepner Oct 17 '19 at 17:50

1 Answers1

4

From the docs, by design slicing out of range indexes don't return errors.

However, out of range slice indexes are handled gracefully when used for slicing:

>>
>> word[4:42]
'on'
>> word[42:]
''

Your assumption is that in order to slice, Python is iterating the indexes of an object and then putting those into a new object, and therefore it should throw an error when it gets to an index that doesn't exist. That makes sense, but when slicing was designed it was specifically designed to not throw an error for out of range indexes and so it doesn't.


And just for fun, check out the source here:

PyObject *
_PySlice_FromIndices(Py_ssize_t istart, Py_ssize_t istop)
{
    PyObject *start, *end, *slice;
    start = PyLong_FromSsize_t(istart);
    if (!start)
        return NULL;
    end = PyLong_FromSsize_t(istop);
    if (!end) {
        Py_DECREF(start);
        return NULL;
    }

    slice = PySlice_New(start, end, NULL);
    Py_DECREF(start);
    Py_DECREF(end);
    return slice;
}

From my limited knowledge of C it is looking for the start/end index and if they don't return it is essentially treating it as if they didn't exist (maximum bounds) by setting them to the true start and end index.

MyNameIsCaleb
  • 4,409
  • 1
  • 13
  • 31
  • Regarding the difference between a single item and a slice: Because `str` is a little weird to begin with, it might make more sense for `""[0]` to return `""` that it would for `[][0]` to return anything, but not enough to make `str.__getitem__` behave *that* differently from `list.__getitem__`. – chepner Oct 17 '19 at 17:47
  • Looking at just the source file I don't see any comments to indicate reasoning, and I imagine slicing has been around a long time so I wouldn't even know where to start to confirm why it was designed the way it was. – MyNameIsCaleb Oct 17 '19 at 17:53