Чому нарізка підрядків з індексом поза діапазоном працює?


88

Чому не 'example'[999:9999]призводить до помилки? З тих пір 'example'[9], як це мотивується?

З цієї поведінки я можу припустити, що 'example'[3], по суті / внутрішньо, не є однаковим 'example'[3:4], навіть якщо обидва результати призводять до одного 'm'рядка.


17
[999:9999]не є індексом, це зріз і має різну семантику. З вступу python: "Вироджені індекси зрізів обробляються витончено: завеликий індекс замінюється розміром рядка, верхня межа менша за нижню межу повертає порожній рядок."
Вубл

2
@Wooble це справжня відповідь
jondavidjohn

2
@Wooble А ти знаєш, чому так? Дякуємо за роз'яснення.
ijverig

Чому? Потрібно було б запитати у Гвідо, але я думаю, що це елегантно мати можливість вважати, що фрагмент - це завжди той самий тип послідовності, що й оригінальна послідовність.
Вубл

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цінностей у списку.
Mark Ransom

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], python повертається 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

@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

Нарізка не перевіряється межами вбудованих типів. І хоча обидва ваші приклади мають однаковий результат, вони працюють по-різному; спробуйте їх зі списком.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.