Learning Goals
3 minBy the end of this lesson you can:
- Pull a single letter out of a string with
word[0],word[3]orword[-1], and count letters withlen(word). - Slice out substrings with
word[1:4],word[:3]andword[3:]— and explain why the stop number is excluded. - Use the reverse-slice
word[::-1]to spell a word backwards, and use it to spot palindromes like"malam"and"katak".
Warm-Up
5 minLast lesson we cleaned and replaced whole strings. Today we peek inside one. Remember from L1-15 how a list lets you reach in with my_list[0]? Strings do the exact same thing.
Quick predict-the-output puzzle. What does Python print?
word = "MALAYSIA" print(word[0]) print(word[-1]) print(word[1:4]) print(len(word))
Show the answer
M A ALA 8
Four little wins. word[0] is the first letter (Python counts from zero — yes, even for strings). word[-1] is the last letter without needing to know the length. word[1:4] grabs letters at positions 1, 2 and 3 — note the 4 is excluded. len(word) counts every character including spaces.
New Concept · Strings Are Lists of Letters
14 minEach letter has an address
Picture the word "MALAM" as a row of pigeon-holes. Each letter sits in a numbered box, starting at 0:
# index: 0 1 2 3 4 # letter: M A L A M # neg: -5 -4 -3 -2 -1
The same trick we used for lists in PY-L1-15 — including negative indices that count from the end — works for strings:
word = "MALAM" print(word[0]) # M (first letter) print(word[2]) # L (third letter) print(word[-1]) # M (last letter) print(word[-2]) # A (second-from-last)
Index 0 is letter one — get used to it
This is the bug that every beginner writes at least once: thinking word[1] gives the first letter. It doesn't. The boxes are numbered like floors in some buildings — there's a Floor 0 before Floor 1. Index 0 = first letter.
Indexing too far · IndexError
Ask for a letter that doesn't exist and Python complains:
word = "MALAM" # length 5, valid indices 0..4 print(word[5])
IndexError: string index out of range
Why? len("MALAM") is 5, but valid indices are 0, 1, 2, 3, 4. The last valid index is always len(word) - 1. Or just use word[-1] and never worry about it.
Slicing · grab a chunk in one go
A slice uses two numbers separated by a colon: word[start:stop]. It returns a new string containing the letters from start up to but not including stop.
word = "MALAYSIA" # 01234567 print(word[0:4]) # MALA (letters 0,1,2,3) print(word[4:8]) # YSIA (letters 4,5,6,7) print(word[1:4]) # ALA (letters 1,2,3)
Why exclude the stop? It means word[0:4] gives you exactly 4 letters — 4 - 0. Always works out clean. You can also drop one end:
name = "Hafiz" print(name[:3]) # Haf (from the start, up to index 3) print(name[2:]) # fiz (from index 2, all the way to the end) print(name[:]) # Hafiz (a full copy)
The famous reverse slice
A slice has a secret third number — a step. word[::2] walks two letters at a time, and the iconic word[::-1] walks backwards, spelling the whole string in reverse:
word = "katak" print(word[::-1]) # → katak word = "Hafiz" print(word[::-1]) # → zifaH
This one-liner is the standard Python party trick. It also gives us the easiest palindrome check ever written:
word = "malam" if word == word[::-1]: print("Palindrome!") else: print("Nope.") # → Palindrome!
One letter → use [i]. Several letters in a row → use [start:stop]. The first index is 0, the last is -1, and stop is always excluded.
Worked Example · Priya's Initials & ID Tag
12 minThe job
Priya is building name badges for the KL Robotics Camp. Each badge has two parts: the camper's initials in capitals (P.K.) and an ID tag made from the last four characters of their birth year (e.g. 2013 → 2013). We'll build a tiny script that takes a first name, last name, and a year and prints both lines.
Step 1 · Grab the first letter of each name
first = "Priya" last = "Kumaran" print(first[0]) # P print(last[0]) # K
Step 2 · Build the initials string
Stitch them together with +:
initials = first[0] + "." + last[0] + "." print(initials) # → P.K.
Step 3 · Take the last 4 characters of a longer string
The slice [-4:] reads as "from four-from-the-end, all the way to the end":
birth = "20130815" # full date as text year_suffix = birth[-4:] # last four characters print(year_suffix) # → 0815
Want the actual year — the first four characters? That's birth[:4]:
year = birth[:4] print(year) # → 2013
Step 4 · Assemble the badge
Save as badge.py:
Code
# badge.py — name badge builder first = "Priya" last = "Kumaran" birth = "20130815" initials = first[0].upper() + "." + last[0].upper() + "." year = birth[:4] tag = "KL-" + year + "-" + last[-3:].upper() print("Initials:", initials) print("ID tag:", tag)
Output
Initials: P.K. ID tag: KL-2013-RAN
Notice the cameos from PY-L1-23 — .upper() still works and we're even chaining it onto a single sliced letter. last[-3:] grabs the last three letters of "Kumaran" — that's "ran" — and we shout it.
Initials, year suffixes, file-extension checks, hiding all but the last 4 digits of a card number — every one of these is just a slice. It's the most-used string tool in real-world Python.
Try It Yourself
13 minThree tasks today. Type each, run it, and only peek at the hint if you're truly stuck.
Set word = "cendol". Print the first letter, the last letter, and the total number of letters — one per line. Use [0], [-1] and len().
Hint
word = "cendol" print(word[0]) print(word[-1]) print(len(word))
You should see c, then l, then 6. len() counts every letter — try it on "teh tarik" too and notice the space counts as 1.
Set phone = "0123456789". Print the area code (first 3 digits) and the last 4 digits — the bit a bank app shows when it says "ending in 6789". Use two slices.
Hint
phone = "0123456789" area = phone[:3] last4 = phone[-4:] print("Area:", area) print("Ending in:", last4)
Expected output: Area: 012 then Ending in: 6789. phone[:3] reads as "from start, up to (not including) index 3". phone[-4:] reads as "from four-from-the-end to the end".
Ask the user for a word with input(). Clean it with .strip().lower() (PY-L1-23 callback!), then print Palindrome! if the cleaned word reads the same forwards and backwards. Otherwise print Not a palindrome.. Try it on katak, malam, and nasi.
Hint
raw = input("Type a word: ") word = raw.strip().lower() if word == word[::-1]: print("Palindrome!") else: print("Not a palindrome.")
The whole magic is word == word[::-1]. The reverse slice [::-1] spells the word backwards; == checks the two are identical character-for-character. Cleaning first means "Malam" and " malam " both still count.
Mini-Challenge · Mei's Crashy Initial Maker
8 minMei wrote a script initials.py to print someone's initials. It works on her own name but crashes on her teammate Bo's name. Read the script, spot the two bugs, and fix them.
# initials.py — Mei's buggy initial maker
first = "Bo"
last = "Tan"
print("Initials:", first[1] + "." + last[1] + ".")
print("Last letter of first name:", first[2])Your fixes should make the script:
- Print
Initials: B.T.(it currently printso.a.on the lucky cases and crashes on others). - Print the actual last letter of
firstusing a negative index, so it works no matter how long the name is. - Not crash on any short name like
"Bo"or"Li".
Stretch. Change the script so it asks the user for their first and last name with input() instead of hard-coding "Bo" and "Tan". Clean with .strip().
Show one possible fix
# initials.py — fixed first = input("First name: ").strip() last = input("Last name: ").strip() print("Initials:", first[0].upper() + "." + last[0].upper() + ".") print("Last letter of first name:", first[-1])
Bug 1 was the classic off-by-one: Mei used [1] for the "first" letter, but index 0 is the first letter. Bug 2 was first[2], which crashes with IndexError for any name shorter than 3 letters. first[-1] never crashes — it always grabs the last letter, however short the name.
Recap
3 minStrings are sequences of characters with numbered slots. word[0] is the first letter, word[-1] the last, and word[start:stop] grabs a chunk — remembering that stop is always excluded. The negative-step slice word[::-1] spells the whole string backwards, which makes palindrome detection a one-line check. Combine these with the cleaning methods from PY-L1-23 and you can pull apart almost any piece of text.
Vocabulary Card
- index
- The position of a character inside a string. The first character is at index
0; the last is at index-1. - slice
[start:stop] - A new string containing the characters from
startup to but not includingstop. - step
[start:stop:step] - The third slice number.
2takes every other character;-1walks the string backwards. len(word)- Returns the total number of characters in
word, including spaces. The last valid index is alwayslen(word) - 1. - IndexError
- The crash you get when you ask for a position that doesn't exist, like
word[99]. Slicing never throws this — it just gives back as much as exists. - palindrome
- A word that reads the same forwards and backwards —
"malam","katak","level". Checked withword == word[::-1].
Homework
4 minCreate a new file username_maker.py — a username generator for a class chat app.
- Ask the user for their first name with
input("First name: "). Clean it with.strip()and store it infirst. - Ask for their last name the same way and store it in
last. - Ask for their birth year as text (e.g.
"2013") and store it inyear. - Build a username made of: the first letter of
firstin lowercase, plus all oflastin lowercase, plus the last two digits ofyear. SoAisyah Rahman 2012becomesarahman12. - Print the username, and also print the length of the username using
len(). - Bonus line: print whether
firstis a palindrome withfirst.lower() == first.lower()[::-1].
Bring username_maker.py next class — in PY-L1-25 we'll combine today's slicing with last lesson's methods to build a real game: Anagram Detective.
Sample · username_maker.py
# username_maker.py — class chat username builder first = input("First name: ").strip() last = input("Last name: ").strip() year = input("Birth year: ").strip() username = first[0].lower() + last.lower() + year[-2:] print("Username:", username) print("Length:", len(username)) is_palindrome = first.lower() == first.lower()[::-1] print("First name is palindrome?", is_palindrome)
The non-negotiables: first[0] for one letter (not first[1]!), year[-2:] for the last two digits, and chaining .lower() onto the slices to keep the username tidy. The palindrome check is the same one-liner from the practice — clean both sides the same way before comparing.