◀ Stage Select World 1 · Stage 1-04

Working with Text

Most real programs are mostly text handling

Names, emails, addresses, log lines, form fields, file contents — the majority of what a program touches is text that arrived in the wrong shape. This stage covers cutting text apart, cleaning it up, putting it back together, and formatting a result someone can read.

Ready?

Step-by-step lessons

Cut, Clean, Rebuild, Present

Four short lessons: getting at pieces of a string, the methods that clean it up, breaking it into parts and gluing them back, and formatting the result with f-strings.

1

Every Character Has a Position

A string is a sequence of characters, each with a position counted from zero. Not one. Zero.

word = "DOJO"
#       0123

word[0]    # "D"
word[3]    # "O"
word[-1]   # "O"  — negative counts back from the end
len(word)  # 4

Because counting starts at zero, the last position is len(word) - 1, never len(word). Reaching for the latter gives you an IndexError, and that off-by-one is the single most common mistake in this stage.

A slice takes a range: word[start:stop] includes the start position and stops before the stop position.

[2:5]

A range

Positions 2, 3 and 4. Never 5.

[:3]

From the start

The first three characters.

[3:]

To the end

Everything from position 3 onwards.

[-4:]

The last four

Counting back from the end.

Why stop-before is the right rule

It looks arbitrary until you notice two things. text[:n] and text[n:] always rejoin into exactly the original, with nothing lost or duplicated. And the length of text[a:b] is simply b - a. Both stop being true if the stop position is included.

Quick check

Given code = "PY-2026", what does code[3:7] give you?

2

Methods: Functions Attached to a Value

A method is a function that belongs to a value. You call it with a dot, and the value it is attached to is the thing it works on.

"  Kenji  ".strip()          # "Kenji"
"KENJI".lower()              # "kenji"
"kenji tanaka".title()       # "Kenji Tanaka"
"a-b-c".replace("-", " ")    # "a b c"
"kenji@example.com".endswith(".com")   # True
"tea, rice".count(",")       # 1

Every one of these hands back a new string. None of them changes the original, because strings in Python are immutable — they cannot be modified in place, ever.

Does nothing

name.upper()

On its own line. The new string is created and immediately thrown away.

Works

name = name.upper()

The result is assigned somewhere, so it survives.

Methods can be chained, because each one returns a string that the next can be called on. Read them left to right, as a sequence of steps: messy.strip().title() is "remove the outer spaces, then fix the capitals".

Quick check

A program runs email.strip() on its own line, then compares email to a stored address and finds no match. Why?

3

Breaking Apart and Putting Back Together

split() cuts a string into a list of pieces. join() glues a list of pieces back into one string. They are exact opposites and they turn up together constantly.

"tea,rice,miso".split(",")      # ["tea", "rice", "miso"]
"Kenji  Tanaka".split()         # ["Kenji", "Tanaka"]  — any whitespace
", ".join(["a", "b", "c"])      # "a, b, c"

split() with nothing in the brackets is a special case worth knowing: it breaks on any run of whitespace and discards empty pieces, so it copes with double spaces, tabs and line breaks without you thinking about it.

join() reads backwards

Everyone gets this the wrong way round at first. The string you call join() on is the glue, and the list of pieces goes inside the brackets. So ", ".join(parts) means "put a comma and a space between every piece". Not parts.join(", ") — that is the other language you are thinking of.

The reason it is worth the awkwardness: join() puts the separator between pieces, never after the last one. Building the same line by hand almost always leaves a trailing comma.

Quick check

You have parts = ["a", "b", "c"] and want "a-b-c". Which line does it?

4

f-strings: Writing the Sentence, Dropping in the Values

Put an f before the opening quote and anything inside {braces} is replaced by its value:

name = "Kenji"
stages = 3

f"{name} cleared {stages} stages"        # "Kenji cleared 3 stages"
f"{name} earned {stages * 200} XP"       # "Kenji earned 600 XP"

The braces hold a whole expression, not just a name, so a small calculation can live right where its result belongs.

After a colon comes a format spec, which controls how the value is displayed. These three cover most of what you will need:

:.2f

Two decimal places

f"{37.5:.2f}" gives 37.50. This is how money prints.

:>6

Right-align in 6

Pads on the left, which is what lines numbers up in a column.

:,

Thousands separator

f"{1234567:,}" gives 1,234,567.

A debugging trick worth stealing

Put an = at the end of the expression inside the braces and Python prints the expression as well as its value: f"{total=}" produces total=37.5. When a program is doing something you did not expect, scattering a few of those beats guessing every time.

Once a sentence has more than one value in it, reach for an f-string.

Quick check

You need a total of 7.5 to print as 7.50. What goes in the braces?

Type it yourself

Code Lab

Four exercises on cutting, cleaning, joining and formatting text — the operations you will reach for more than any others.

0 of 4 cleared

Real Python runs right here in your browser — nothing to install, nothing sent to a server. The interpreter downloads once the first time you press Run, then stays cached.

01

Cut a piece out

To do

Every character in a string has a position, counted from zero. word[0] is the first character.

word[2:5] is a slice: start at 2, stop before 5. Leaving a side blank means "all the way" — word[:3] is the first three characters and word[3:] is everything from position 3 onwards. Negative positions count back from the end, so word[-1] is the last character.

code = "PY-2026-KENJI"
code[0] → "P"
code[3:7] → "2026"
code[-5:] → "KENJI"

Your task: from ticket, pull out prefix (the first two characters), year (the four digits) and name (everything after the last dash). Print each on its own line.

your_code.py
Python
Hint

Count the positions: P is 0, Y is 1, the dash is 2, so the year starts at 3. ticket[3:7] gets the four digits, and ticket[-5:] grabs the last five characters.

Output

      
    02

    Clean up what people typed

    To do

    Text from humans arrives messy: stray spaces, inconsistent capitals, the wrong separators. String methods fix that. A method is a function attached to a value, called with a dot:

    "  Kenji ".strip()      →  "Kenji"    (spaces off both ends)
    "KENJI".lower() → "kenji"
    "kenji".upper() → "KENJI"
    "kenji".title() → "Kenji"
    "a-b".replace("-", " ") → "a b"

    Every one of these hands back a new string. The original is untouched — strings in Python can never be modified in place, which is why you always assign the result to something.

    Your task: turn messy into clean: no spaces at either end, and capitalised as a name. The result must be exactly Kenji Tanaka.

    your_code.py
    Python
    Hint

    Two problems, two methods, and they chain: messy.strip() removes the outside spaces, and .title() fixes the capitals. Write it as one expression, left to right.

    Output
    
          
      03

      Break apart and put back together

      To do

      split() cuts a string into a list of pieces, and join() glues a list back into one string.

      "a,b,c".split(",")        →  ["a", "b", "c"]
      " ".join(["a", "b"]) → "a b"

      join() reads backwards the first few times: the string you call it on is the glue, and the list goes inside the brackets. ", ".join(parts) means "put a comma and a space between every piece".

      Your task: raw holds three tags separated by commas. Split them into tags, then build headline by joining them with " · " (a space, a middle dot, a space). Print the number of tags, then the headline.

      your_code.py
      Python
      Hint

      tags = raw.split(",") gives you the list. Then " · ".join(tags) glues them back with the separator you want. len(tags) counts them.

      Output
      
            
        04

        Drop values straight into text

        To do

        Building a sentence out of values with commas and plus signs gets ugly fast. An f-string lets you write the sentence and drop the values in where they belong. Put f before the opening quote and any {expression} inside gets replaced by its value.

        name = "Kenji"
        xp = 600
        f"{name} has {xp} XP" → "Kenji has 600 XP"

        Anything can go in the braces, including a calculation: f"{xp * 2} XP". A format spec after a colon controls how a number is displayed — f"{value:.2f}" shows exactly two decimal places, which is how you print money without a mess of digits.

        Your task: build summary as an f-string reading exactly Kenji cleared 3 stages for 600 XP, where the 600 is calculated rather than typed. Then build price_line reading exactly Total: 37.50, with two decimal places. Print both.

        your_code.py
        Python
        Hint

        f"{name} cleared {stages} stages for {stages * xp_each} XP" — the maths can live inside the braces. For the money, f"Total: {total:.2f}".

        Output
        
              
          Boss round

          Assignment: Tidy the Sign-Up

          One graded task. Pass every check and the stage is cleared and the title is yours — one more stage towards the Python Practitioner certificate.

          This one is graded. You can see 2 of the checks up front, and 4 stay hidden until you run. The hidden ones test the same job from angles you have not been shown, so code that genuinely solves the problem clears this and code shaped around the visible examples does not. Pass them all and the stage is yours.

          Tidy up a sign-up

          To do

          A sign-up form has handed you one messy line. Pull it apart, clean each piece, and print a tidy record.

          The raw value is " kenji TANAKA | KENJI@Example.COM | Osaka " — three fields separated by a vertical bar, with random spacing and capitals.

          Create these four variables:

          • full_nameKenji Tanaka, capitalised as a name
          • emailkenji@example.com, all lowercase
          • cityOsaka
          • initialsKT, built from the first letter of each part of the name

          Then print exactly three lines, using f-strings:

          Kenji Tanaka (KT)
          kenji@example.com
          City: Osaka

          Every field must be derived from raw. The hidden checks confirm the pieces were actually extracted rather than retyped.

          your_code.py
          Python
          Hint

          Each piece from split still has spaces around it, so .strip() every one. Then .title() the name and .lower() the email. For the initials, split the cleaned name and take character [0] of each part.

          Output
          
                
            Lock it in

            Guess the Term

            Read the clues and name the concept. The fewer clues you need, the more points you score.

            Round 1 Score 0
            Keep it handy

            Strings Quick Reference

            The whole stage on one screen.

            Positions

            [0]

            First character

            Counting starts at zero.

            [-1]

            Last character

            Negatives count back from the end.

            [a:b]

            Slice

            From a, stopping before b. Length is b - a.

            Cleaning

            .strip()

            Whitespace off both ends.

            aA

            .lower() .upper() .title()

            Force the capitalisation.

            .replace(a, b)

            Every occurrence, not just the first.

            Structure

            .split(sep)

            String to list. Empty brackets splits on any whitespace.

            sep.join(list)

            List to string. The glue goes on the outside.

            len(text)

            How many characters.

            f-strings

            f""

            f"{name} is {age}"

            Braces hold any expression.

            .2f

            f"{total:.2f}"

            Exactly two decimal places.

            =

            f"{total=}"

            Prints the name and the value, for debugging.

            Notification