Skip to content

Working with Strings

Summary

This chapter provides a comprehensive treatment of Python strings. Students will learn string indexing, negative indexing, slicing, and immutability. The chapter covers essential string methods (upper, lower, strip, split, join, find, replace), string formatting techniques, iteration over strings, and string comparison. These text-processing skills are fundamental to nearly every Python program.

Concepts Covered

This chapter covers the following 25 concepts from the learning graph:

  1. String Basics
  2. String Indexing
  3. Negative Indexing
  4. String Slicing
  5. Slice Notation
  6. String Immutability
  7. String Length
  8. String Methods
  9. Upper and Lower Methods
  10. Strip Method
  11. Split Method
  12. Join Method
  13. Find and Replace Methods
  14. Startswith and Endswith
  15. String Iteration
  16. String Comparison
  17. Escape Characters
  18. Raw Strings
  19. Multiline Strings
  20. String Formatting
  21. Format Method
  22. String Validation Methods
  23. In Operator for Strings
  24. Character Methods
  25. String Reversal

Prerequisites

This chapter builds on concepts from:


Monty says: Let's code this!

Monty Welcome back, coders! Get ready to become a text wizard. Strings are everywhere in programming — usernames, messages, web pages, even DNA sequences. By the end of this chapter, you'll be slicing, dicing, searching, and formatting text like a pro. Let's string together some awesome skills!

String Basics

A string is a sequence of characters. That's it. Letters, numbers, spaces, punctuation, emoji — if you can type it, you can put it in a string. In Python, you create a string by wrapping text in quotes:

1
2
3
greeting = "Hello, world!"
name = 'Alice'
number_as_text = "42"

Notice you can use either double quotes ("...") or single quotes ('...'). Python doesn't care which one you pick, as long as you start and end with the same type. Most Python programmers use double quotes by convention, but single quotes are handy when your string contains a double quote:

1
dialogue = 'She said, "Python is amazing!"'

A string can be empty, too:

1
empty = ""

An empty string has zero characters, but it's still a valid string. Think of it like an empty box — there's nothing inside, but the box is real.

Here are a few important things to know about strings right away:

  • Strings are ordered — each character has a specific position
  • Strings are immutable — once created, you can't change individual characters (more on this soon)
  • Strings can contain any Unicode character — letters, digits, symbols, and even emoji

String Length

How many characters are in a string? Python's built-in len() function tells you the string length:

1
2
message = "Hello!"
print(len(message))  # Output: 6

Every character counts, including spaces and punctuation:

1
2
sentence = "Hi there!"
print(len(sentence))  # Output: 9

That's 2 letters + 1 space + 5 letters + 1 exclamation mark = 9 characters. The len() function is one you'll reach for constantly. It's your string measuring tape.

Accessing Characters: String Indexing

Since a string is a sequence, each character sits at a numbered position called an index. Here's the key: Python starts counting at zero, not one. This is called zero-based indexing.

Let's visualize the string "PYTHON":

Character P Y T H O N
Index 0 1 2 3 4 5

You access a character using square brackets:

1
2
3
4
word = "PYTHON"
print(word[0])   # P
print(word[1])   # Y
print(word[5])   # N

If you try to access an index that doesn't exist, Python raises an IndexError:

1
print(word[6])   # IndexError: string index out of range

String indexing is like seats on a bus. Seat 0 is the first seat, seat 1 is the second, and so on. If the bus only has 6 seats (indices 0-5), asking for seat 6 gets you nowhere.

Diagram: String Indexing Visualizer

String Indexing Visualizer MicroSim

Type: microsim sim-id: string-indexing-visualizer
Library: p5.js
Status: Specified

Bloom Level: Apply (L3) Bloom Verb: demonstrate, identify

Learning Objective: Students will be able to determine the character at any given index (positive or negative) within a string.

Purpose: An interactive visualization where students type a string and click on individual character boxes to see both the positive and negative index for that position.

Layout:

  • Top section: Text input field where the student can type any string (default: "PYTHON")
  • Middle section: Row of character boxes, each showing one character of the string in a large font
  • Above each box: The positive index (0, 1, 2, ...)
  • Below each box: The negative index (-6, -5, -4, ...)
  • Bottom section: Display area showing string[index] = character when a box is clicked
  • A slider or number input labeled "Index:" where the student can enter a positive or negative integer, and the corresponding character box highlights

Interactive elements:

  • Click any character box to highlight it and display its positive index, negative index, and value
  • Type a positive or negative integer in the "Index:" input to highlight the corresponding box
  • Edit the string in the text input to see a new string visualized instantly
  • If the entered index is out of range, show a red "IndexError!" message

Visual elements:

  • Character boxes have alternating light blue and light green backgrounds
  • Highlighted box gets a gold border and slight scale-up animation
  • Positive indices displayed in blue above boxes
  • Negative indices displayed in red below boxes
  • IndexError message displayed in red with a shake animation

Color scheme: Blue for positive indices, red for negative indices, gold highlight for selected box Responsive: Boxes scale based on string length and window width

Instructional Rationale: Direct manipulation of index values supports the Apply level by requiring students to map between index numbers and character positions. Showing both positive and negative indices simultaneously helps students build a mental model of bidirectional access. Immediate IndexError feedback teaches boundaries without crashing a real program.

Negative Indexing

What if you want the last character of a string, but you don't know how long it is? You could write word[len(word) - 1], but Python gives you a shortcut: negative indexing.

Negative indices count backward from the end of the string. The last character is at index -1, the second-to-last is -2, and so on:

Character P Y T H O N
Positive Index 0 1 2 3 4 5
Negative Index -6 -5 -4 -3 -2 -1
1
2
3
4
word = "PYTHON"
print(word[-1])   # N (last character)
print(word[-2])   # O (second to last)
print(word[-6])   # P (first character)

Negative indexing is incredibly handy. Need the last character of a filename to check its extension? Just use filename[-1]. Need the last three characters? We'll cover that next with slicing.

String Slicing

String slicing lets you extract a portion of a string — a substring. Instead of grabbing one character at a time, you can grab a whole chunk.

The slice notation uses a colon inside the square brackets:

1
string[start:stop]

This gives you characters from index start up to (but not including) index stop. Think of it like a fence: start is where you begin, stop is where you stop — you don't include the post at stop.

1
2
3
4
word = "PYTHON"
print(word[0:3])   # PYT (indices 0, 1, 2)
print(word[2:5])   # THO (indices 2, 3, 4)
print(word[1:4])   # YTH (indices 1, 2, 3)

You can also leave out start or stop to slice from the beginning or to the end:

1
2
3
print(word[:3])    # PYT (from start to index 3)
print(word[3:])    # HON (from index 3 to end)
print(word[:])     # PYTHON (a copy of the whole string)

Monty says: You've got this!

Monty The "up to but not including" rule trips up a lot of beginners. Here's a trick to remember it: the number of characters you get equals stop - start. So word[1:4] gives you 4 - 1 = 3 characters. Neat, right?

Extended Slice Notation

You can add a third number — the step — to skip characters:

1
string[start:stop:step]
1
2
3
4
word = "PYTHON"
print(word[0:6:2])   # PTO (every other character)
print(word[::2])     # PTO (same thing, shorter)
print(word[1::2])    # YHN (every other, starting at index 1)

Negative slicing works too:

1
2
print(word[-3:])     # HON (last three characters)
print(word[:-2])     # PYTH (everything except last two)

Here's a handy reference table for common slicing patterns:

Slice Meaning Example with "PYTHON"
s[0:3] First three characters "PYT"
s[:3] First three characters (shorthand) "PYT"
s[3:] Everything from index 3 onward "HON"
s[-3:] Last three characters "HON"
s[:-1] Everything except the last character "PYTHO"
s[::2] Every other character "PTO"
s[::-1] The entire string reversed "NOHTYP"
s[:] A copy of the full string "PYTHON"

Diagram: String Slicing Playground

String Slicing Playground MicroSim

Type: microsim sim-id: string-slicing-playground
Library: p5.js
Status: Specified

Bloom Level: Apply (L3) Bloom Verb: demonstrate, predict

Learning Objective: Students will be able to use slice notation to extract substrings, predict the result of a slice expression, and experiment with start, stop, and step parameters.

Purpose: An interactive playground where students can adjust start, stop, and step values using sliders and immediately see which characters are selected in a visual string display.

Layout:

  • Top section: Editable text input for the string (default: "COMPUTER SCIENCE")
  • Middle section: Row of character boxes (like the indexing visualizer) showing each character with positive index above and negative index below
  • Three sliders below the character boxes:
  • Start slider (range: -len to len, default: 0)
  • Stop slider (range: -len to len, default: len)
  • Step slider (range: -len to len, excluding 0, default: 1)
  • Below sliders: Display showing the current slice notation string[start:stop:step] and the resulting substring
  • A "Challenge" button that shows a target substring and asks the student to set the sliders to produce it

Interactive elements:

  • Drag any slider to change start, stop, or step values
  • Selected characters are highlighted in the character box row in real time
  • Characters included in the slice get a gold background; excluded characters are dimmed
  • An arrow overlay on the character boxes shows the direction (left-to-right or right-to-left depending on step sign)
  • Challenge mode displays a target string and turns green when the student's slice matches

Visual elements:

  • Character boxes use alternating colors; selected characters glow gold
  • Sliders labeled clearly with current values
  • The slice notation string updates live: e.g., "COMPUTER SCIENCE"[2:8:1] = "MPUTER"
  • Step direction indicated by arrow overlays on the character boxes
  • Challenge mode target shown in a green bordered box

Color scheme: Gold for selected characters, gray for excluded, green for correct challenge answer Responsive: Canvas and sliders scale with window width; character boxes resize for long strings

Instructional Rationale: Slider-based manipulation supports the Apply level by letting students experiment freely with all three slice parameters and immediately see the visual result. The spatial highlighting on the character row makes abstract index arithmetic concrete. Challenge mode provides goal-directed practice.

String Immutability

Here's a concept that surprises many new programmers: strings in Python are immutable. That means once a string is created, you cannot change its individual characters. You can't reach in and swap one letter for another.

1
2
word = "Hello"
word[0] = "J"   # TypeError: 'str' object does not support item assignment

If you want a modified version of a string, you create a new string:

1
2
3
word = "Hello"
new_word = "J" + word[1:]   # "Jello"
print(new_word)             # Jello

Why is string immutability a thing? It makes strings safer and more efficient behind the scenes. Python can share string data in memory without worrying that some other part of your program will change it unexpectedly. Think of it like a printed poster — if you want to change a word on it, you don't erase the ink; you print a new poster.

Monty says: Let's debug this together!

Monty This is one of the most common mistakes beginners make: trying to change a character inside a string with my_string[3] = "x". Python will always throw a TypeError. Remember — to "change" a string, you build a new one from pieces of the old one.

Escape Characters

What if you need to put a quote mark inside a string that's wrapped in the same kind of quotes? Or what about a newline or a tab? That's where escape characters come in. An escape character starts with a backslash (\) followed by a special code:

Escape Sequence What It Produces
\n New line (moves to next line)
\t Tab (horizontal space)
\\ A literal backslash
\' A literal single quote
\" A literal double quote
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
print("Line one\nLine two")
# Output:
# Line one
# Line two

print("Name:\tAlice")
# Output:
# Name:   Alice

print("She said, \"Wow!\"")
# Output:
# She said, "Wow!"

Raw Strings

Sometimes you want backslashes to be treated literally — no escape sequences. This is common when working with file paths on Windows or regular expressions. A raw string is prefixed with r:

1
2
3
4
5
6
# Regular string: \n becomes a newline
print("C:\new_folder")   # C:
                          # ew_folder

# Raw string: \n stays as \n
print(r"C:\new_folder")  # C:\new_folder

Raw strings tell Python: "Don't interpret any backslashes. Just take everything literally."

Multiline Strings

For text that spans multiple lines, use triple quotes — either """...""" or '''...''':

1
2
3
4
5
6
poem = """Roses are red,
Violets are blue,
Python is awesome,
And so are you!"""

print(poem)

Multiline strings preserve line breaks and spacing exactly as you type them. They're perfect for long messages, documentation, or any text that needs to span several lines.

String Methods

Now we get to the really powerful stuff. String methods are built-in functions that every string carries with it. You call them using dot notation: my_string.method_name(). Python has dozens of string methods, and they all return new strings (remember, strings are immutable!).

Let's explore the most important ones.

Upper and Lower Methods

The upper and lower methods change the case of every letter in a string:

1
2
3
name = "Alice Smith"
print(name.upper())   # ALICE SMITH
print(name.lower())   # alice smith

These are incredibly useful for case-insensitive comparisons:

1
2
3
user_input = "YES"
if user_input.lower() == "yes":
    print("User said yes!")

There are a few related case methods worth knowing:

Method What It Does Example
.upper() All uppercase "hello" becomes "HELLO"
.lower() All lowercase "HELLO" becomes "hello"
.title() Capitalize each word "hello world" becomes "Hello World"
.capitalize() Capitalize first letter only "hello world" becomes "Hello world"
.swapcase() Flip every letter's case "Hello" becomes "hELLO"

Strip Method

The strip method removes whitespace (spaces, tabs, newlines) from both ends of a string:

1
2
3
4
5
messy = "   Hello, world!   "
clean = messy.strip()
print(clean)        # "Hello, world!"
print(len(messy))   # 19
print(len(clean))   # 13

You can also strip from just one side:

  • .lstrip() — removes leading (left) whitespace
  • .rstrip() — removes trailing (right) whitespace

Why does this matter? Because user input is often messy. Someone types " Alice " with extra spaces, and your program needs to handle it gracefully. The strip() method is your cleanup crew.

Split Method

The split method breaks a string into a list of smaller strings based on a separator:

1
2
3
4
5
6
7
sentence = "Python is awesome"
words = sentence.split()        # Split on whitespace (default)
print(words)                    # ['Python', 'is', 'awesome']

csv_data = "Alice,Bob,Charlie"
names = csv_data.split(",")     # Split on commas
print(names)                    # ['Alice', 'Bob', 'Charlie']

The split() method is a workhorse for parsing text data. Got a comma-separated list? Split on commas. Got a sentence? Split on spaces. Got a file path? Split on slashes.

Join Method

The join method is the opposite of split() — it takes a list of strings and glues them together with a separator:

1
2
3
4
5
6
7
words = ['Python', 'is', 'awesome']
sentence = " ".join(words)
print(sentence)                 # "Python is awesome"

names = ['Alice', 'Bob', 'Charlie']
csv_line = ",".join(names)
print(csv_line)                 # "Alice,Bob,Charlie"

Notice the syntax: you call .join() on the separator string, not on the list. This feels a bit backward at first, but it makes sense when you think about it — the separator is what defines how the pieces connect.

Diagram: Split and Join Visualizer

Split and Join Visualizer MicroSim

Type: microsim sim-id: split-join-visualizer
Library: p5.js
Status: Specified

Bloom Level: Understand (L2) Bloom Verb: explain, demonstrate

Learning Objective: Students will be able to explain how split() breaks a string into a list and how join() reassembles a list into a string, and predict the output for given inputs and separators.

Purpose: An animated visualization that shows the split and join operations step by step, helping students see how a single string becomes a list of parts and vice versa.

Layout:

  • Top half: "Split" section
  • Input field for a string (default: "one,two,three")
  • Input field for separator (default: ",")
  • A "Split!" button
  • Animation area: shows the original string breaking apart at separator positions, pieces flying to form a list display ['one', 'two', 'three']
  • Bottom half: "Join" section
  • Display of the current list from the split above
  • Input field for join separator (default: " - ")
  • A "Join!" button
  • Animation area: shows list items sliding together with the separator inserted between them, forming a new string "one - two - three"

Interactive elements:

  • Edit the input string and separator, then click "Split!" to see the animated split
  • Edit the join separator, then click "Join!" to see the animated join
  • Each piece in the split list is a draggable card that students can reorder before joining
  • A "Swap" button that toggles between split mode and join mode

Visual elements:

  • String displayed as a row of character boxes
  • Separator characters highlighted in red before the split animation
  • During split animation, the string visually tears apart at separator positions
  • List items shown as colored cards
  • During join animation, cards slide together with separator cards inserted between them
  • Final joined string animates into a continuous row

Color scheme: Blue for string characters, red for separator characters, green for list item cards Responsive: Canvas scales with window width; text sizes adjust for long strings

Instructional Rationale: Animation of the split/join process supports the Understand level by making the invisible string manipulation visible. Showing split and join as inverse operations reinforces that they are complementary. Allowing students to reorder list items before joining adds an element of experimentation.

Find and Replace Methods

Need to search inside a string? The find and replace methods have you covered.

The .find() method returns the index of the first occurrence of a substring, or -1 if it's not found:

1
2
3
message = "Hello, world!"
print(message.find("world"))    # 7
print(message.find("Python"))   # -1 (not found)

The .replace() method swaps every occurrence of one substring with another:

1
2
3
text = "I like cats. Cats are great."
new_text = text.replace("cats", "dogs")
print(new_text)   # "I like dogs. Cats are great."

Wait — why didn't "Cats" get replaced? Because .replace() is case-sensitive. "cats" and "Cats" are different strings. If you want to replace both, you'd need to handle each case or convert to lowercase first.

You can also limit the number of replacements:

1
2
text = "ha ha ha ha"
print(text.replace("ha", "HO", 2))   # "HO HO ha ha"

Startswith and Endswith

The startswith and endswith methods check whether a string begins or ends with a specific substring. They return True or False:

1
2
3
4
filename = "report.pdf"
print(filename.endswith(".pdf"))      # True
print(filename.endswith(".docx"))     # False
print(filename.startswith("report"))  # True

These are perfect for checking file extensions, URL protocols, or any kind of prefix/suffix matching:

1
2
3
url = "https://www.python.org"
if url.startswith("https://"):
    print("Secure connection!")

String Formatting

Building strings from a mix of text and variables is something you'll do constantly. String formatting gives you clean, readable ways to combine them.

f-strings (Formatted String Literals)

The most modern and popular approach is the f-string. Just put an f before the opening quote and use curly braces {} to insert expressions:

1
2
3
4
name = "Alice"
age = 16
print(f"My name is {name} and I am {age} years old.")
# Output: My name is Alice and I am 16 years old.

You can put any Python expression inside the curly braces:

1
2
3
x = 7
print(f"The square of {x} is {x ** 2}.")
# Output: The square of 7 is 49.

The Format Method

Before f-strings, Python programmers used the format method:

1
2
3
template = "My name is {} and I am {} years old."
print(template.format("Alice", 16))
# Output: My name is Alice and I am 16 years old.

You can also use numbered or named placeholders:

1
2
3
4
5
print("{0} loves {1}, and {1} loves {0}".format("Alice", "Bob"))
# Output: Alice loves Bob, and Bob loves Alice

print("{name} scored {score}%".format(name="Charlie", score=95))
# Output: Charlie scored 95%

Both f-strings and .format() work great. In this course, we'll mostly use f-strings since they're shorter and easier to read. But you'll see .format() in plenty of existing Python code.

Monty says: Let's debug this together!

Monty f-strings are your best friend for clean output. Instead of writing print("Score: " + str(score) + " points"), just write print(f"Score: {score} points"). Shorter, cleaner, and no need to convert numbers to strings manually!

Diagram: String Formatting Comparison

String Formatting Comparison MicroSim

Type: microsim sim-id: string-formatting-comparison
Library: p5.js
Status: Specified

Bloom Level: Apply (L3) Bloom Verb: apply, demonstrate

Learning Objective: Students will be able to construct formatted strings using f-strings and the .format() method, and choose the appropriate formatting approach for a given task.

Purpose: A side-by-side comparison tool where students define variables and a template, then see the output of both f-string and .format() approaches simultaneously.

Layout:

  • Top section: Variable definition area with three text inputs:
  • Variable 1: name and value (default: name = "Alice")
  • Variable 2: name and value (default: age = 16)
  • Variable 3: name and value (default: score = 95.5)
  • Middle section: Two columns
  • Left column: "f-string" — shows the f-string syntax with editable template (default: f"Hello {name}, age {age}, score: {score}")
  • Right column: ".format()" — shows the equivalent .format() syntax, auto-generated
  • Bottom section: Output display showing the result string, identical for both approaches
  • A "Try it!" button that evaluates and displays the result
  • A gallery of example templates students can click to load

Interactive elements:

  • Edit variable values and see the output update
  • Edit the f-string template and see the .format() equivalent auto-generate
  • Click example templates from a gallery (e.g., "Name badge", "Score report", "Email greeting")
  • Toggle "Show number formatting" to see examples with decimal places, padding, and alignment

Visual elements:

  • Variable values highlighted in gold within both template strings
  • Curly braces and format specifiers highlighted in blue
  • Output string displayed in a large, clean font
  • Side-by-side layout with a visual "equals" sign between the two approaches

Color scheme: Gold for variable values, blue for format syntax, green for output Responsive: Columns stack vertically on narrow screens

Instructional Rationale: Side-by-side comparison supports the Apply level by letting students see two equivalent approaches simultaneously, building flexibility. Editable templates with instant feedback encourage experimentation. The example gallery provides scaffolded practice for common formatting patterns.

String Iteration

Since a string is a sequence of characters, you can loop through it one character at a time. This is called string iteration, and it uses the for loop you learned in Chapter 4:

1
2
3
word = "PYTHON"
for letter in word:
    print(letter)

Output:

1
2
3
4
5
6
P
Y
T
H
O
N

You can also loop with indices using range() and len():

1
2
3
word = "PYTHON"
for i in range(len(word)):
    print(f"Index {i}: {word[i]}")

Output:

1
2
3
4
5
6
Index 0: P
Index 1: Y
Index 2: T
Index 3: H
Index 4: O
Index 5: N

Here's a practical example — counting how many times a letter appears in a string:

1
2
3
4
5
6
7
text = "mississippi"
count = 0
for char in text:
    if char == "s":
        count += 1
print(f"The letter 's' appears {count} times.")
# Output: The letter 's' appears 4 times.

(Python also has a built-in .count() method that does this in one line: text.count("s"). But writing the loop yourself helps you understand what's happening under the hood.)

The In Operator for Strings

The in operator checks whether a substring exists inside a string. It returns True or False:

1
2
3
4
message = "Hello, world!"
print("world" in message)     # True
print("Python" in message)    # False
print("Hello" in message)     # True

You can also use not in:

1
2
if "spam" not in message:
    print("No spam here!")    # This will print

The in operator is one of Python's most readable features. Code like if "error" in log_message: reads almost like English.

String Comparison

You can compare strings using the standard comparison operators you learned in Chapter 3. String comparison in Python works character by character, using each character's Unicode value:

1
2
3
4
print("apple" == "apple")    # True
print("apple" == "Apple")    # False (case matters!)
print("apple" < "banana")    # True (alphabetical order)
print("A" < "a")             # True (uppercase comes before lowercase in Unicode)

Python compares strings lexicographically — like alphabetical order, but based on Unicode values. Since uppercase letters have lower Unicode values than lowercase letters, "A" (65) is "less than" "a" (97).

Here's the comparison order, from lowest to highest Unicode values:

  1. Digits: "0" through "9" (values 48-57)
  2. Uppercase letters: "A" through "Z" (values 65-90)
  3. Lowercase letters: "a" through "z" (values 97-122)

For case-insensitive comparison, convert both strings to the same case first:

1
2
3
4
user_input = "Alice"
stored_name = "alice"
if user_input.lower() == stored_name.lower():
    print("Names match!")     # This will print

String Validation Methods

Python gives you a set of string validation methods that test whether a string meets certain criteria. They all return True or False:

Method Returns True If... Example
.isalpha() All characters are letters "Hello" is True, "Hello3" is False
.isdigit() All characters are digits "123" is True, "12.3" is False
.isalnum() All characters are letters or digits "Hello3" is True, "Hello!" is False
.isspace() All characters are whitespace " " is True, " hi " is False
.isupper() All letters are uppercase "HELLO" is True, "Hello" is False
.islower() All letters are lowercase "hello" is True, "Hello" is False

These are perfect for validating user input:

1
2
3
4
5
6
age_input = input("Enter your age: ")
if age_input.isdigit():
    age = int(age_input)
    print(f"You are {age} years old!")
else:
    print("That's not a valid number!")

Character Methods

Every character in a string is actually a tiny string of length 1. Python provides a couple of handy character methods for converting between characters and their numeric Unicode values:

  • ord(char) — returns the Unicode number for a character
  • chr(number) — returns the character for a Unicode number
1
2
3
4
5
6
7
print(ord("A"))     # 65
print(ord("a"))     # 97
print(ord("0"))     # 48

print(chr(65))      # A
print(chr(97))      # a
print(chr(128013))  # a snake emoji on many systems

These are useful for tasks like shifting characters (think secret codes!) or checking whether a character falls in a certain range.

Monty says: Let's code this!

Monty Here's a neat trick: since uppercase letters are numbered 65-90 and lowercase are 97-122, the difference is always 32. So chr(ord("A") + 32) gives you "a". That's basically what .lower() does behind the scenes!

String Reversal

How do you reverse a string? Python doesn't have a built-in .reverse() method for strings (remember, strings are immutable!), but string reversal is incredibly easy with slice notation:

1
2
3
word = "PYTHON"
reversed_word = word[::-1]
print(reversed_word)          # NOHTYP

The [::-1] slice says "start at the end, go to the beginning, stepping backward by 1." It's one of Python's most elegant tricks.

You can use this to check if a string is a palindrome — a word that reads the same forward and backward:

1
2
3
4
5
6
7
def is_palindrome(text):
    cleaned = text.lower().replace(" ", "")
    return cleaned == cleaned[::-1]

print(is_palindrome("racecar"))       # True
print(is_palindrome("hello"))         # False
print(is_palindrome("A man a plan a canal Panama"))  # True

Diagram: Palindrome Checker

Palindrome Checker MicroSim

Type: microsim sim-id: palindrome-checker
Library: p5.js
Status: Specified

Bloom Level: Analyze (L4) Bloom Verb: analyze, compare

Learning Objective: Students will be able to apply string reversal and cleaning techniques to determine whether a given string is a palindrome, and trace through the comparison step by step.

Purpose: An interactive palindrome checker that visually shows the step-by-step process of cleaning, reversing, and comparing a string to determine if it reads the same forwards and backwards.

Layout:

  • Top section: Text input for a word or phrase (default: "racecar")
  • Middle section: Three-stage visualization:
  • Stage 1 "Clean": Shows the original string, then the cleaned version (lowercase, no spaces) with removed characters fading out
  • Stage 2 "Reverse": Shows the cleaned string on top and the reversed string below, with animated arrows showing the reversal
  • Stage 3 "Compare": Characters from the original and reversed strings are compared side by side, with matching characters lighting up green
  • Bottom section: Large "Palindrome!" or "Not a palindrome" verdict with animation

Interactive elements:

  • Type any word or phrase in the input
  • Click "Check" to start the step-by-step animation
  • Click "Step" to advance one stage at a time
  • A library of example palindromes to try (e.g., "racecar", "A man a plan a canal Panama", "Was it a car or a cat I saw?")

Visual elements:

  • Characters displayed as individual boxes in a row
  • Cleaning stage: spaces and punctuation fade to transparent; uppercase letters animate to lowercase
  • Reversal stage: boxes animate from original positions to reversed positions with a flip animation
  • Comparison stage: pairs of boxes light up green (match) or red (no match) from outside in
  • Verdict displayed with confetti animation for palindromes

Color scheme: Green for matching characters, red for mismatches, gold for the "Palindrome!" message Responsive: Character boxes scale based on string length and window width

Instructional Rationale: Step-by-step visualization of the cleaning, reversal, and comparison process supports the Analyze level by making each stage of the algorithm explicit. Students can trace the logic visually rather than just seeing a boolean result. The example library provides varied practice, including phrases with spaces and mixed case.

Practical Example: Cleaning User Input

Let's bring several concepts together with a real-world example. Suppose you're building a simple contact form and need to clean up a user's name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
raw_input = "   aLiCe   sMiTh   "

# Step 1: Strip extra whitespace from both ends
stripped = raw_input.strip()
print(stripped)           # "aLiCe   sMiTh"

# Step 2: Split into individual words
parts = stripped.split()
print(parts)              # ['aLiCe', 'sMiTh']

# Step 3: Capitalize each word properly
proper_parts = []
for part in parts:
    proper_parts.append(part.capitalize())
print(proper_parts)       # ['Alice', 'Smith']

# Step 4: Join back together
clean_name = " ".join(proper_parts)
print(clean_name)         # "Alice Smith"

Or, in one clean line using the .title() method:

1
2
clean_name = "   aLiCe   sMiTh   ".strip().title()
print(clean_name)         # "Alice Smith"

That's the power of method chaining — calling multiple methods in a row. Each method returns a new string, and you immediately call the next method on that result.

String Methods Reference Table

Here's a comprehensive reference for the string methods covered in this chapter:

Method Description Returns
s.upper() Converts to uppercase str
s.lower() Converts to lowercase str
s.title() Capitalizes each word str
s.capitalize() Capitalizes first character str
s.strip() Removes whitespace from both ends str
s.lstrip() Removes whitespace from left str
s.rstrip() Removes whitespace from right str
s.split(sep) Splits string into list list
sep.join(list) Joins list into string str
s.find(sub) Index of first occurrence (-1 if not found) int
s.replace(old, new) Replaces occurrences str
s.startswith(prefix) Checks if starts with prefix bool
s.endswith(suffix) Checks if ends with suffix bool
s.count(sub) Counts occurrences of substring int
s.isalpha() True if all letters bool
s.isdigit() True if all digits bool
s.isalnum() True if all letters/digits bool
s.isspace() True if all whitespace bool
s.isupper() True if all uppercase bool
s.islower() True if all lowercase bool

Diagram: String Methods Explorer

String Methods Explorer MicroSim

Type: microsim sim-id: string-methods-explorer
Library: p5.js
Status: Specified

Bloom Level: Apply (L3) Bloom Verb: apply, experiment

Learning Objective: Students will be able to apply common string methods to a given input string, predict the output before seeing it, and explain what each method does.

Purpose: An interactive sandbox where students type a string, select a method from a menu, provide any required arguments, and see the result — with an option to predict the output first.

Layout:

  • Top section: Text input for the string (default: " Hello, World! ")
  • Middle section: Grid of method buttons organized by category:
  • Case methods: upper(), lower(), title(), capitalize(), swapcase()
  • Whitespace methods: strip(), lstrip(), rstrip()
  • Search methods: find(), replace(), count(), startswith(), endswith()
  • Split/Join: split(), join()
  • Validation: isalpha(), isdigit(), isalnum(), isspace(), isupper(), islower()
  • When a method requiring arguments is selected, an argument input field appears
  • Bottom section:
  • "Predict" area: students type what they think the output will be
  • "Run" button: shows the actual output
  • Comparison: highlights whether the prediction was correct

Interactive elements:

  • Click any method button to select it
  • Enter arguments when prompted (e.g., separator for split, substring for find)
  • Type a prediction, then click "Run" to compare
  • A "History" panel on the side shows previous method calls and results

Visual elements:

  • Method buttons color-coded by category (blue for case, green for whitespace, orange for search, purple for validation)
  • Correct predictions get a green check animation
  • Incorrect predictions show the correct answer in red with a brief explanation
  • History panel shows a scrollable list of previous operations

Color scheme: Category-coded buttons, green for correct predictions, red for incorrect Responsive: Button grid adjusts column count based on window width

Instructional Rationale: Predict-then-reveal interaction supports the Apply level by requiring students to mentally execute the method before seeing the answer. The category-organized button grid helps students build a mental map of available methods. The history panel lets students compare results across different methods applied to the same string.

Putting It All Together: Text Analysis

Let's combine everything you've learned into a mini text analysis project. Here's a function that takes a sentence and reports interesting statistics:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def analyze_text(text):
    print(f"Text: '{text}'")
    print(f"Length: {len(text)} characters")
    print(f"Words: {len(text.split())}")
    print(f"Uppercase: {text.upper()}")
    print(f"Reversed: {text[::-1]}")
    print(f"Starts with 'The': {text.startswith('The')}")
    print(f"Contains 'python': {'python' in text.lower()}")

    # Count vowels
    vowels = 0
    for char in text.lower():
        if char in "aeiou":
            vowels += 1
    print(f"Vowels: {vowels}")

analyze_text("The Python programming language is fun")

Output:

1
2
3
4
5
6
7
8
Text: 'The Python programming language is fun'
Length: 38 characters
Words: 6
Uppercase: THE PYTHON PROGRAMMING LANGUAGE IS FUN
Reversed: nuf si egaugnal gnimmargorp nohtyP ehT
Starts with 'The': True
Contains 'python': True
Vowels: 11

Monty says: You've got this!

Monty Amazing work, coder! You've just learned how to index, slice, search, format, validate, and reverse strings. These skills show up in almost every Python program you'll ever write. From processing user input to analyzing data files, strings are your bread and butter. On to the next chapter!

Key Takeaways

  • A string is an ordered, immutable sequence of characters. Create them with single, double, or triple quotes.
  • String indexing starts at 0. Use negative indexing (starting at -1) to count from the end.
  • String slicing with [start:stop:step] extracts substrings. The stop index is not included.
  • Strings are immutable — you can't change individual characters. Create new strings instead.
  • len() returns the number of characters in a string.
  • String methods like .upper(), .lower(), .strip(), .split(), .join(), .find(), and .replace() are powerful tools for text processing.
  • .startswith() and .endswith() check prefixes and suffixes.
  • Use f-strings (f"Hello {name}") or the .format() method for clean string formatting.
  • Escape characters (\n, \t, \\) insert special characters. Raw strings (r"...") disable escaping.
  • Multiline strings use triple quotes and preserve line breaks.
  • String iteration with for loops lets you process one character at a time.
  • The in operator checks if a substring exists within a string.
  • String comparison is case-sensitive and uses Unicode values for ordering.
  • Validation methods (.isalpha(), .isdigit(), etc.) check string content.
  • ord() and chr() convert between characters and Unicode numbers.
  • Reverse a string with the slice [::-1].
Check Your Understanding: What does 'hello world'[6:] return?

It returns "world". The slice starts at index 6 (the 'w') and goes to the end of the string. Remember: 'h' is index 0, 'e' is 1, 'l' is 2, 'l' is 3, 'o' is 4, ' ' is 5, and 'w' is 6.

Check Your Understanding: Why does my_string[0] = 'X' cause an error?

Because strings are immutable in Python. You cannot change individual characters of a string. Instead, you'd create a new string: my_string = 'X' + my_string[1:]. This builds a brand-new string using the character 'X' followed by everything from index 1 onward in the original string.

Check Your Understanding: What's the difference between 'Hello'.find('lo') and 'lo' in 'Hello'?

.find('lo') returns the index where the substring starts (in this case, 3), or -1 if it's not found. The in operator returns a boolean (True or False). Use .find() when you need to know where something is. Use in when you just need to know if it's there.