Почему Python допускает индексы срезов вне диапазона для последовательностей? - PullRequest
0 голосов
/ 10 февраля 2019

Так что я просто наткнулся на то, что мне кажется странной особенностью Python, и хотел кое-что прояснить.

Следующие манипуляции с массивами в некоторой степени имеют смысл:

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]

Я полагаю, что на самом деле это просто добавление этого значения в конец, верно?
Однако, почему я могу это сделать?

p[20:22] = [5,6]
p = [1,2,3,4,5,6]

И даже более того:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]

Это просто неверная логика.Похоже, это должно выдать ошибку!

Есть какое-нибудь объяснение?
-Это просто странная вещь, которую делает Python?
-Есть ли какая-то цель?
-Или я думаю об этом не так?

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

Часть вопроса относительно индексов вне диапазона

Логика среза автоматически обрезает индексы по длине последовательности.

Для удобства было сделано, чтобы индексы среза проходили мимо конечных точек.,Было бы больно иметь диапазон проверки каждого выражения, а затем настраивать пределы вручную, поэтому Python сделает это за вас.

Рассмотрим вариант использования, когда требуется отображать не более первых 50 символов текстаmessage.

Простой способ (что сейчас делает Python):

preview = msg[:50]

Или сложный способ (проверьте лимит самостоятельно):

n = len(msg)
preview = msg[:50] if n > 50 else msg

Реализация вручнуюэту логику для настройки конечных точек будет легко забыть, легко ошибиться (обновление 50 в двух местах), будет многословно и будет медленным.Python перемещает эту логику во внутренности, где она лаконична, автоматическая, быстрая и правильная.Это одна из причин, по которой я люблю Python: -)

Часть вопроса, касающаяся несоответствия длины назначений длине ввода

ОП также хотел знать обоснование для разрешения назначений, например p[20:100] = [7,8]где цель назначения имеет другую длину (80), чем длина замещающих данных (2).

Проще всего увидеть мотивацию по аналогии со строками.Рассмотрим "five little monkeys".replace("little", "humongous").Обратите внимание, что у цели «маленький» есть только шесть букв, а у «огромных» - девять.Мы можем сделать то же самое со списками:

>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'

Все это сводится к удобству.

До введения методов copy () и clear () , это были популярные идиомы:

s[:] = []           # clear a list
t = u[:]            # copy a list

Дажетеперь мы используем это для обновления списков при фильтрации:

s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values

Надеюсь, что эти практические примеры дают хорошее представление о том, почему срез работает так, как он работает.

0 голосов
/ 10 февраля 2019

В документации есть ваш ответ:

s[i:j]: срез s от i до j (примечание (4))

(4) Срез s от i до j определяется как последовательность элементов с индексом k такая, что i <= k < j. Если i или j больше len(s), используйте len(s).Если i опущен или None, используйте 0.Если j опущен или None, используйте len(s).Если i больше или равно j, срез пустой.

Документация IndexError подтверждает это поведение:

исключение IndexError

Возникает, когда нижний индекс последовательности выходит за пределы диапазона.( Индексы среза молча обрезаются до допустимого диапазона; , если индекс не является целым числом, TypeError повышается.)

По существу, такие вещи, как p[20:100]сокращается до p[len(p):len(p].p[len(p):len(p] - это пустой фрагмент в конце списка, и присвоение ему списка изменит конец списка, чтобы он содержал указанный список.Таким образом, это работает как добавление / расширение исходного списка.

Это поведение аналогично тому, что происходит, когда вы назначаете список пустому срезу в любом месте в исходном списке.Например:

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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...