Почему работает нарезка подстроки с индексом вне диапазона?

88

Почему не 'example'[999:9999]приводит к ошибке? Поскольку 'example'[9]это так, какова мотивация этого?

Исходя из этого поведения, я могу предположить, что 'example'[3]это по существу / внутренне не то же самое, что и 'example'[3:4], хотя оба результата приводят к одной и той же 'm'строке.

Иджвериг
источник
17
[999:9999]не индекс, это срез и имеет другую семантику. Из вступления к python: «Вырожденные индексы срезов обрабатываются изящно: слишком большой индекс заменяется размером строки, верхняя граница меньше нижней возвращает пустую строку».
Wooble
2
@Wooble, вот и настоящий ответ
jondavidjohn
2
@Wooble А ты знаешь, почему это так? Благодарим Вас за разъяснения.
ijverig
Зачем? Вы должны спросить Гвидо, но я думаю, что это изящно - иметь возможность предположить, что срез всегда представляет собой последовательность того же типа, что и исходная последовательность, я.
Wooble
1
@Lapinot да, я написал код, который зависит от этого поведения. К сожалению, я не могу вспомнить точный код, поэтому не могу сказать почему. Вероятно, имел отношение к подстрокам; получение пустой строки может быть именно тем, что вы иногда хотите.
Марк Рэнсом

Ответы:

68

Вы правы! 'example'[3:4]и 'example'[3]принципиально отличаются, и нарезка за пределы последовательности (по крайней мере, для встроенных модулей) не вызывает ошибки.

Поначалу это может показаться удивительным, но, если подумать, это имеет смысл. Индексирование возвращает один элемент, а разрезание возвращает подпоследовательность элементов. Поэтому, когда вы пытаетесь проиндексировать несуществующее значение, возвращать нечего. Но когда вы нарезаете последовательность за пределами границ, вы все равно можете вернуть пустую последовательность.

Отчасти сбивает с толку то, что строки ведут себя немного иначе, чем списки. Посмотрите, что происходит, когда вы делаете то же самое со списком:

>>> [0, 1, 2, 3, 4, 5][3]
3
>>> [0, 1, 2, 3, 4, 5][3:4]
[3]

Здесь разница очевидна. В случае строк результаты кажутся идентичными, потому что в Python нет такой вещи, как отдельный символ вне строки. Одиночный символ - это просто строка из 1 символа.

(Точную семантику нарезки вне диапазона последовательности см. В ответе Мильсона .)

отправитель
источник
1
Индекс вне допустимого диапазона мог быть возвращен Noneвместо ошибки - это обычное соглашение Python, когда вам нечего возвращать.
Марк Рэнсом,
8
@MarkRansom, это правда; но при возврате Noneв этом случае будет сложнее отличить индекс вне границ от Noneзначения внутри списка. Но даже если бы для этого существовал обходной путь, мне остается ясно, что возвращение пустой последовательности - это то, что нужно делать, когда дан фрагмент, выходящий за границы. Это аналогично объединению двух непересекающихся множеств.
senderle
Чтобы внести ясность, я не говорил, что вы ошибались. Я вижу вашу точку зрения о Noneценностях в списке.
Марк Рэнсом,
1
@MarkRansom, я знаю - извините, если я защищался. На самом деле я просто хотел повод обратиться к теории множеств :).
senderle
4
Ой, за исключением того, что я сказал «союз» вместо «пересечение».
senderle
31

Ради добавления ответа, указывающего на надежный раздел в документации :

Учитывая такое выражение среза, как s[i:j:k],

Срез s от i до j с шагом k определяется как последовательность элементов с x = i + n*kтаким индексом , что 0 <= n < (j-i)/k. Другими словами, индексы i, i+k, i+2*k, i+3*kи так далее, останавливаясь , когда J достигается (но никогда не включая J ). Когда k положительно, i и j уменьшаются до, len(s)если они больше

если вы пишете s[999:9999], питон возвращается , s[len(s):len(s)]так len(s) < 999и ваш шаг положителен ( 1- по умолчанию).

мгильсон
источник
Предположительно, когда kположительный, iи когда они jтакже увеличиваются, -len(s)когда они меньше? напримерs = 'bac'; s[-100:2] == s[-len(s):2]
Chris_Rands 04
@Chris_Rands Когда kположительно, Python будет масштабироваться iи jтак , чтобы они соответствовали границе последовательности. В вашем примере s[-100:2] == s[0:2]( == s[-len(s):2]кстати). Аналогично s[-100:100] == s[0:2].
tylerc0816
Хорошо, спасибо. Это лучший ответ на комментарий @ speedplane выше.
senderle
8

Нарезка не проверяется границами встроенными типами. И хотя оба ваших примера, кажется, дают одинаковый результат, они работают по-разному; вместо этого попробуйте их со списком.

Игнасио Васкес-Абрамс
источник