Python числа Последовательности
shoplist = ['яблоки', 'манго', 'морковь', 'бананы']
name = 'swaroop'
print('Элементы с 1 по -1:', shoplist[1:-1])
Почему получается Элементы с 1 по -1: ['манго', 'морковь']
А при print('Элемент 1 -', shoplist[1]) и print('Элемент -1 -', shoplist[-1])
Получается Элемент 1 - манго и Элемент -1 - бананы
Ответы (1 шт):
Что происходит
Принято соглашение: при указании диапазона левый конец (начало) включается в диапазон, а правый конец (конец) исключается. Например:
$ python Python 3.10.0 (default, Oct 16 2021, 12:17:56) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. @>>> a = list(range(10)) @>>> a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Если попросить срез со второго по пятый элемент (индексация с нуля, значения совпадают с индексами), то получатся значения 2, 3, 4, но не 5:
@>>> a[2:5] [2, 3, 4] @>>> a[2] 2 @>>> a[5] 5
Если попросить срез a[1:-1], то получатся значения от единицы до восьми. Последнее значение по индексу 9 (он же индекс -1) не включено:
@>>> a[1:-1] [1, 2, 3, 4, 5, 6, 7, 8] @>>> a[1] 1 @>>> a[-1] 9
Почему
Это соглашение. Просто авторы (я подозреваю, один единственный автор) приняли решение: все диапазоны должны быть полуинтервалами. В коде a[i:j], в нотации a[i,j). Левый конец включается, правый исключается.
Этому соглашению следует язык и его библиотека: срезы, range и многое другое. Оно вам может нравиться или не нравиться, но так сделано.
Это удобно когда ...
... надо узнать сколько элементов в срезе [i, j). Это просто j - i. Если бы правый конец был включён, прилось бы добавлять единицу.
... надо работать с пустым срезом.
a[i:i]- пустой срез. Если бы правый конец был включён, прилось бы писатьa[i:i - 1], что немного странно.... надо разбить список на части.
a[:i]– левая часть,a[i:]– правая часть. Они не пересекаются по i-тому элементу.
Это очень не удобно когда ...
- ... надо перевернуть срез задом наперёд. Был срез
a[i:j], он же перевёрнутый –a[j - 1:i - 1:-1]. Это тихий ужас, но такое требуется не часто. Кроме того можно написать так:a[i:j][::-1].
История
У полуинтервалов длинная история. В C массивы байт (и вообще массивы) представлены парой указателей: первый включён, второй исключён. Из него даже читать нельзя. В С++ такая же история с итераторами. Правый конец всегда исключён. Например, vector::end() ссылается на элемент за концом вектора: итератор есть, читать из него нельзя.
В Питон полуинтервалы, я думаю, перекочевали из C. Сам CPython написан на C, разработчикам удобно что оба языка одинаково работают с интервалами.
Так уж сложилось, что языки (FORTRAN, ALGOL, PASCAL) в которых интервалы включают правый конец, выбыли из гонки (но есть и исключения). А их конкурент с нулевой индексацией и полуинтервалами жив и породил кучу последователей (это я про C).
Вот в доказательство письмо Дейкстры, в котором он поясняет как хороши полуинтервалы и индексация с нуля: https://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD831.html. Ему можно верить, он придумал половину того, что сейчас называется программированием.