13  Strings

13.1 The Wall

AI’s string slicing gave you the wrong substring. It used text[3:7] and you expected 4 characters but got something different. In another case, AI used .split(), .join(), .replace(), and .strip() in a chain so long you could not tell what the final result would be without running it.

Strings are the most common data type in AI-generated code, every user interaction, every API response, every file path is a string. If you cannot predict what string operations do, you cannot debug half the code AI produces.

This chapter fixes that.

NoteComing from Think Python?

If you worked through Think Python, Direct AI, you already used string methods like .lower() and .split() to process user input in your projects. Here we explore strings more thoroughly, how they work internally, the full range of methods, and when to reach for each one.

13.2 Thinking Session

13.2.1 Getting Oriented

NoteThinking Session Prompt

Explain Python strings in depth. I know they are text in quotes, but I want to understand: are they like lists? Can I change individual characters? How does indexing and slicing work on strings compared to lists?

Your AI should explain that strings are immutable sequences. You can index and slice them like lists, but you cannot change individual characters. text[0] = "X" fails. To modify a string, you create a new one. This matters because AI sometimes writes code that tries to modify strings in place.

13.2.2 Go Deeper

NoteThinking Session Prompt

What are the most useful string methods in Python? Focus on the ones that AI uses most: split(), join(), strip(), replace(), startswith(), endswith(), find(), upper(), lower(), count(). For each one, show me what it does and what it returns.

TipWhat to Look For

Key pattern: all string methods return new strings (strings are immutable). text.split() returns a list. " ".join(words) returns a string. text.replace("old", "new") returns a new string. The original is unchanged. If your AI implies these modify the original string, that is wrong.

NoteThinking Session Prompt

Explain string slicing in Python. What does text[2:5] give me? What about text[::-1]? I want to be able to look at any slice notation in AI-generated code and predict the result.

13.2.3 Challenge It

NoteThinking Session Prompt

What does each line produce?

"hello world".split()
" ".join(["hello", "world"])
"  hello  ".strip()
"hello world".replace("world", "Python")
"hello"[1:4]
"hello"[::-1]
"hello world".title()
"python".count("o")
TipWhat to Look For

split() gives ["hello", "world"]. join() gives "hello world". strip() gives "hello". replace() gives "hello Python". [1:4] gives "ell" (index 1, 2, 3, stop is exclusive). [::-1] gives "olleh" (reversed). title() gives "Hello World". count("o") gives 1.

13.2.4 What You Should Have Learned

  • Strings are immutable, all methods return new strings
  • Indexing and slicing work like lists: text[start:stop:step]
  • split() makes a list, join() makes a string
  • strip() removes whitespace, replace() substitutes text
  • startswith() and endswith() are cleaner than slicing for checks
  • f-strings embed expressions: f"Hello {name}"

13.3 The Gap

Strings are how your chatbot processes everything the user types. Every message is a string. Every response is built from strings. Now you can read AI-generated string operations and predict the output. When AI chains five string methods together, you can trace through each step.

In the Building Session, you will add pattern matching to your chatbot using string methods.

13.4 Building Session

13.4.1 The Spec

Add string-based pattern matching to your chatbot:

  • Detect greetings using startswith()
  • Extract topics from messages using split() and keyword detection
  • Format responses with proper capitalisation using title() and capitalize()
  • Add a “repeat” command that echoes the last message in different formats (upper, lower, title, reversed)

13.4.2 Prompt It

NoteBuilding Session Prompt

Update my chatbot to v1.3. Enhance the text processing:

  • Use startswith() for greeting detection instead of checking exact matches
  • Add a process_message() function that cleans input (strip, normalise whitespace) and extracts keywords using split()
  • Add a “repeat” command that shows the last user message in: UPPER, lower, Title Case, and reversed
  • Format bot responses so the first letter is always capitalised

Keep existing features.

13.4.3 Read the Code

Your AI will produce something like this:

def process_message(text):
    """Clean and analyse a message."""
    cleaned = " ".join(text.split())  # normalise whitespace
    words = cleaned.lower().split()
    return cleaned, words


def format_repeat(text):
    """Show text in multiple formats."""
    lines = [
        f"  UPPER:    {text.upper()}",
        f"  lower:    {text.lower()}",
        f"  Title:    {text.title()}",
        f"  Reversed: {text[::-1]}",
    ]
    return "\n".join(lines)
TipWhat to Notice

" ".join(text.split()) is a common idiom. It splits on any whitespace and rejoins with single spaces, normalising ” hello world ” to “hello world”. text[::-1] reverses the string. "\n".join(lines) joins a list of strings with newlines between them. All these methods return new strings. The original is never modified.

13.4.4 Stretch It

NoteBuilding Session Prompt

Add a “find” command that takes a word and searches the conversation history for messages containing that word. Show the matching messages with the search word highlighted by wrapping it in asterisks using replace().

13.5 Your Chatbot So Far

  • Ch 1-11: Full features, functions, history, random responses
  • Ch 12: Loop-based processing, quiz
  • Ch 13: String pattern matching, text normalisation, repeat command

13.6 Quick Reference

# String methods (return new strings)
"hello world".split()           # ["hello", "world"]
" ".join(["a", "b"])            # "a b"
"  hi  ".strip()                # "hi"
"hello".replace("l", "L")      # "heLLo"
"hello".upper()                 # "HELLO"
"HELLO".lower()                 # "hello"
"hello world".title()           # "Hello World"
"hello".startswith("he")        # True
"hello".endswith("lo")          # True
"hello".find("ll")              # 2 (index)
"hello".count("l")              # 2

# Slicing
text = "Python"
text[0]         # "P"
text[1:4]       # "yth"
text[::-1]      # "nohtyP"

# f-strings
f"Hello {name}"
f"{value:.2f}"
f"{'centred':^20}"