Learning Goals
3 minBy the end of this lesson you can:
- Read any single character out of a string using its index.
- Use a negative index to count from the end without knowing the length.
- Take a run of characters with a slice, and explain why the end is excluded.
Warm-Up · Counting the Letters
5 minYou already know len() from lists. It works on text too. Predict both lines before you run it.
city = "Rome" print(len(city)) print(city + "!")
Answer
Output
6 Rome!
Six characters, and + joins text end to end. Neither of those lets you pull out just the M. That is what today adds.
New Concept · Every Character Has a Number
12 minThink of a string as a row of lockers. Each locker holds one character, and each has a number painted on the door. Give Python the number, it opens that door.
The lockers in "Rome"
M e l a k a 0 1 2 3 4 5 -6 -5 -4 -3 -2 -1
Counting starts at 0, not 1. That trips everyone once. The first character is at 0, so the last is at len(city) - 1.
city = "Rome" print(city[0]) print(city[3]) print(city[-1])
Output
M a a
The bottom row of numbers is the useful trick. city[-1] is the last character whatever the word is, so you never have to work out the length first.
Slicing — a run of characters
One character at a time is slow. A slice takes a run of them, written text[start:stop].
city = "Rome" print(city[0:3]) print(city[3:6])
Output
Mel aka
city[0:3] gives characters 0, 1 and 2 — three characters, stopping before 3. Read it as "from 0, up to but not including 3". The pay-off is that city[0:3] and city[3:6] fit together with no gap and no overlap, which is exactly what you want when splitting something in two.
Leave a number out and Python assumes the end you did not mention.
city = "Rome" print(city[:3]) print(city[3:]) print(city[:])
Output
Mel aka Rome
Why it matters. Real text arrives in fixed shapes all the time — a student ID where the first three letters are the branch, a date written 2026-09-01, a phone number whose last four digits identify the account. Slicing is how you pull the part you need out of the middle.
Worked Example · Reading a Student ID
12 minAdvaspire student IDs look like ADV002-0147. The shape never changes, so every part sits at a known position.
# student_id.py - pull the parts out of a fixed-shape ID student_id = "ADV002-0147" academy = student_id[0:3] # first three characters branch = student_id[3:6] # the branch number number = student_id[7:] # everything after the dash print("Full ID: ", student_id) print("Academy: ", academy) print("Branch: ", branch) print("Student: ", number) # The dash sits at index 6, which is why the branch stops there print("Separator:", student_id[6]) # Negative indexes read the tail without counting print("Last two digits:", student_id[-2:])
Output
Full ID: ADV002-0147 Academy: ADV Branch: 002 Student: 0147 Separator: - Last two digits: 47
Every slice above is a decision about where one field ends and the next begins. Write the positions down before you write the code and the slices almost write themselves.
student_id[99] raises IndexError: string index out of range. But student_id[0:99] quietly gives you the whole string. Slices clamp themselves to what exists, which is forgiving — and occasionally hides a mistake.
Try It Yourself
13 minAsk for a word with input(). Print its first character, its last character, and how many characters it has.
Hint
word = input("Type a word: ") print("First:", word[0]) print("Last:", word[-1])
A date arrives as "2026-09-01". Using slices only, print the year, the month and the day on three lines. Then print them back in the order 01/09/2026 on one line.
Check your positions on paper first: the two dashes sit at index 4 and index 7.
Mini-Challenge 🔥 · Debug: The Off-By-One Initials
8 minTheo wants the initials of a full name. His script runs without crashing, which makes it worse — the answer is simply wrong. Find two mistakes.
# initials.py - buggy
full_name = "Ella Izzati"
first_initial = full_name[1]
last_initial = full_name[-2]
print("Initials:", first_initial + last_initial)Answer
It prints Initials: uu — two wrong letters, no error.
full_name[1]is the second character. Counting starts at 0, so theNis at index0. This is the classic off-by-one.full_name[-2]is the second from the end. The last character is-1. But note the real bug hiding underneath: even-1would givei, the end of "Izzati", not its first letter. The surname initial is at index6, just after the space.
The working version, with the surname found properly:
# initials.py - fixed full_name = "Ella Izzati" first_initial = full_name[0] last_initial = full_name[6] print("Initials:", first_initial + last_initial)
Output
Initials: NI
Hard-coding 6 only works for this one name. Lesson 3 shows you how to find the space yourself, so any name works.
Recap
3 minA string is a row of characters, numbered from 0 forwards and from -1 backwards. text[i] gives you one character; text[start:stop] gives you a run, stopping just before stop. Leave either number out and Python uses that end of the string.
Vocabulary Card
- index
- The position of one character, counting from 0.
- negative index
- A position counted from the end, where
-1is the last character. - slice
- A run of characters taken with
[start:stop], excludingstop. - IndexError
- The error raised when you ask for a position the string does not have.
Homework
4 minWrite ic_reader.py. A IC number is written 021015-14-5678: six digits of birth date, two for place of birth, four more.
- Store one IC number in a variable.
- Using slices, print the birth year, month and day on separate lines.
- Print the last four digits.
- Print the birth date tidily as
15/10/02.
Bring the file next class. Use a made-up number, never a real one.
Sample · ic_reader.py
# ic_reader.py - read the parts of an IC number by position ic = "021015-14-5678" year = ic[0:2] month = ic[2:4] day = ic[4:6] place = ic[7:9] last_four = ic[-4:] print("Year: ", year) print("Month:", month) print("Day: ", day) print("Place:", place) print("Last four digits:", last_four) print("Birth date:", day + "/" + month + "/" + year)
Output
Year: 02 Month: 10 Day: 15 Place: 14 Last four digits: 5678 Birth date: 15/10/02
Your variable names may differ. What matters is that every field comes from a slice, and that last_four uses a negative index rather than counting to 14.