Hai ragione! 'example'[3:4]
e 'example'[3]
sono fondamentalmente diversi e il taglio al di fuori dei limiti di una sequenza (almeno per i built-in) non causa un errore.
All'inizio potrebbe essere sorprendente, ma ha senso se ci pensi. L'indicizzazione restituisce un singolo elemento, ma il sezionamento restituisce una sottosequenza di elementi. Quindi, quando provi a indicizzare un valore inesistente, non c'è niente da restituire. Ma quando si taglia una sequenza al di fuori dei limiti, è comunque possibile restituire una sequenza vuota.
Parte di ciò che crea confusione qui è che le stringhe si comportano in modo leggermente diverso dalle liste. Guarda cosa succede quando fai la stessa cosa su un elenco:
>>> [0, 1, 2, 3, 4, 5][3]
3
>>> [0, 1, 2, 3, 4, 5][3:4]
[3]
Qui la differenza è evidente. Nel caso delle stringhe, i risultati sembrano essere identici perché in Python non esiste un singolo carattere al di fuori di una stringa. Un singolo carattere è solo una stringa di 1 carattere.
(Per la semantica esatta dell'affettatura al di fuori dell'intervallo di una sequenza, vedere la risposta di mgilson .)
[999:9999]
non è un indice, è una sezione e ha una semantica diversa. Dall'introduzione di Python: "Gli indici di slice degeneri vengono gestiti con grazia: un indice troppo grande viene sostituito dalla dimensione della stringa, un limite superiore più piccolo del limite inferiore restituisce una stringa vuota."