Dlaczego działa wycinanie podciągów z indeksem poza zakresem?

88

Dlaczego nie 'example'[999:9999]powoduje błędu? Skoro 'example'[9]tak, jaka jest motywacja za tym?

Na podstawie tego zachowania mogę założyć, że 'example'[3]zasadniczo / wewnętrznie nie jest to to samo co 'example'[3:4], mimo że oba skutkują tym samym 'm'ciągiem.

ijverig
źródło
17
[999:9999]nie jest indeksem, to wycinek i ma inną semantykę. Z wprowadzenia do Pythona: „Zdegenerowane indeksy wycinków są obsługiwane z wdziękiem: indeks, który jest zbyt duży, jest zastępowany rozmiarem ciągu, a górna granica mniejsza niż dolna zwraca pusty ciąg”.
Wooble
2
@Wooble to jest właściwa odpowiedź
jondavidjohn
2
@Wooble A czy wiesz, dlaczego tak jest? Dziękuję Ci za wyjaśnienie.
ijverig
Czemu? Musiałbyś zapytać Guido, ale myślę, że to eleganckie, aby móc założyć, że wycinek jest zawsze tego samego typu, co oryginalna sekwencja, ja.
Wooble
1
@Lapinot tak, napisałem kod, który zależy od tego zachowania. Niestety nie pamiętam dokładnego kodu, więc nie mogę powiedzieć, dlaczego. Prawdopodobnie miało to coś wspólnego z podciągami; otrzymanie pustego ciągu może być czasami dokładnie tym, czego chcesz.
Mark Ransom

Odpowiedzi:

68

Masz rację! 'example'[3:4]i 'example'[3]są zasadniczo różne, a wycinanie poza granice sekwencji (przynajmniej w przypadku elementów wbudowanych) nie powoduje błędu.

Na początku może to być zaskakujące, ale ma sens, gdy się nad tym zastanowić. Indeksowanie zwraca pojedynczy element, ale wycinanie zwraca podciąg elementów. Więc kiedy próbujesz zindeksować nieistniejącą wartość, nie ma nic do zwrócenia. Ale kiedy wycinasz sekwencję poza granicami, nadal możesz zwrócić pustą sekwencję.

Część tego, co jest niejasne, polega na tym, że łańcuchy zachowują się nieco inaczej niż listy. Zobacz, co się dzieje, gdy robisz to samo z listą:

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

Tutaj różnica jest oczywista. W przypadku łańcuchów wyniki wyglądają na identyczne, ponieważ w Pythonie nie ma czegoś takiego jak pojedynczy znak poza łańcuchem. Pojedynczy znak to tylko 1-znakowy ciąg.

(Aby poznać dokładną semantykę wycinania poza zakresem sekwencji, zobacz odpowiedź mgilsona ).

nadawca
źródło
1
Indeks spoza zakresu mógł zostać zwrócony Nonezamiast błędu - taka jest zwykła konwencja Pythona, gdy nie masz nic do zwrócenia.
Mark Ransom
8
@MarkRansom, to prawda; ale zwrócenie Nonew tym przypadku utrudniłoby odróżnienie indeksu spoza zakresu od Nonewartości wewnątrz listy. Ale nawet gdyby istniało obejście tego problemu, pozostaje dla mnie jasne, że zwrócenie pustej sekwencji jest właściwą rzeczą do zrobienia, gdy otrzyma się wycinek poza zakresem. Jest to analogiczne do wykonania sumy dwóch rozłącznych zbiorów.
senderle
Żeby było jasne, nie powiedziałem, że się mylisz. Widzę twój punkt widzenia na temat Nonewartości na liście.
Mark Ransom
1
@MarkRansom, wiem - przepraszam, jeśli zabrzmiało to defensywnie. Naprawdę chciałem tylko wymówki, żeby odnieść się do teorii mnogości :).
senderle
4
Och, tylko że powiedziałem „zjednoczenie” zamiast „skrzyżowanie”.
senderle
31

W celu dodania odpowiedzi wskazującej na solidną sekcję w dokumentacji :

Biorąc pod uwagę wyrażenie takie jak s[i:j:k]:

Wycinek s od i do j z krokiem k jest definiowany jako sekwencja elementów z x = i + n*ktakim indeksem 0 <= n < (j-i)/k. Innymi słowy, indeksy i, i+k, i+2*k, i+3*ki tak dalej, zatrzymując się, gdy j osiągnie (ale nigdy w tym j ). Kiedy k jest dodatnie, i i j są redukowane do, len(s)jeśli są większe

jeśli piszesz s[999:9999], python wraca s[len(s):len(s)]od, len(s) < 999a twój krok jest dodatni ( 1- domyślny).

mgilson
źródło
Przypuszczalnie, kiedy kjest pozytywny, ia jtakże wzrasta, -len(s)gdy są mniejsze? np.s = 'bac'; s[-100:2] == s[-len(s):2]
Chris_Rands
@Chris_Rands Gdy kjest dodatnia, Python skaluje się ii jdopasowuje do granic sekwencji. W twoim przykładzie s[-100:2] == s[0:2]( == s[-len(s):2]przy okazji). Podobnie s[-100:100] == s[0:2].
tylerc0816
Fajnie dzięki. To lepsza odpowiedź na powyższy komentarz @ speedplane.
nadawca
8

Krojenie nie jest sprawdzane przez typy wbudowane. I chociaż oba twoje przykłady wydają się mieć ten sam wynik, działają one inaczej; zamiast tego wypróbuj je z listą.

Ignacio Vazquez-Abrams
źródło