Skip to content
elephantoo

Strings & f-strings

Lesson 4 of 38 13 min read

Indexing and slicing, everyday string methods, split/join, Unicode and powerful f-string formatting.


Text is the most common kind of data you'll handle: names, messages, file contents, web pages, JSON. Python's str type is powerful and pleasant to use. In this lesson you'll learn to create, slice, search, transform and format strings — and master f-strings, the modern way to build text.

Creating strings#

Single and double quotes are identical; pick one style and use the other when your text contains quotes:

Python
a = 'single'
b = "double"
c = "It's easy"
d = 'She said "hi"'
e = "Line one\nLine two\tTabbed"    # \n newline, \t tab
f = """Triple quotes
span multiple lines."""
print(e)
print(f)
Output
Line one
Line two	Tabbed
Triple quotes
span multiple lines.

A backslash starts an escape sequence. When you want backslashes kept literally — Windows paths, regular expressions — use a raw string with an r prefix:

Python
print("C:\\new\\folder")   # escaped backslashes
print(r"C:\new\folder")    # raw string: same result, easier to read
Output
C:\new\folder
C:\new\folder

Indexing and slicing#

A string is a sequence of characters, numbered from 0. Negative indexes count from the end:

Python
s = "Elephantoo"
print(s[0], s[4], s[-1], s[-2])
print(len(s))
Output
E h o o
10

A slice s[start:stop:step] extracts a piece. start is included, stop is excluded, and any part can be omitted:

Python
s = "Elephantoo"
print(s[0:5])    # first five characters
print(s[:5])     # same: start defaults to 0
print(s[5:])     # from index 5 to the end
print(s[-3:])    # last three
print(s[::2])    # every second character
print(s[::-1])   # reversed
Output
Eleph
Eleph
antoo
too
Eehno
ootnahpelE

Indexing past the end raises IndexError, but slicing never does — s[5:100] simply returns what's there.

Strings are immutable#

You can't change a string in place. s[0] = "e" raises TypeError. Every string method returns a new string, so remember to keep the result:

Python
name = "  ada lovelace  "
name.strip()             # result thrown away!
print(repr(name))
name = name.strip().title()
print(name)
Output
'  ada lovelace  '
Ada Lovelace

The methods you'll use every day#

Python
text = "Python is fun. Python is powerful."

print(text.upper())
print(text.lower())
print(text.count("Python"))
print(text.find("fun"))           # index of first match, -1 if missing
print(text.replace("Python", "Py"))
print(text.startswith("Py"), text.endswith("!"))
print("fun" in text)              # membership test
Output
PYTHON IS FUN. PYTHON IS POWERFUL.
python is fun. python is powerful.
2
10
Py is fun. Py is powerful.
True False
True

Cleaning and checking:

Python
raw = "   hello world \n"
print(repr(raw.strip()))      # remove whitespace at both ends
print(repr(raw.lstrip()))     # left only
print("42".isdigit(), "4a".isdigit())
print("hello".isalpha(), "Hello World".istitle())
print("readme.md".removesuffix(".md"))
Output
'hello world'
'hello world \n'
True False
True True
readme

Splitting and joining

split() breaks a string into a list; join() glues a list of strings back together. They're opposites and you'll use them constantly:

Python
csv_line = "Ada,Grace,Linus"
names = csv_line.split(",")
print(names)

words = "  many   spaces here ".split()   # no argument: split on any whitespace
print(words)

print(" | ".join(names))
print("-".join(["2026", "09", "30"]))
Output
['Ada', 'Grace', 'Linus']
['many', 'spaces', 'here']
Ada | Grace | Linus
2026-09-30

Note that join is called on the separator, and every item must already be a string: ", ".join(str(n) for n in [1, 2, 3]).

f-strings: formatting done right#

An f-string (formatted string literal) starts with f and evaluates any expression inside {}:

Python
name = "Ada"
age = 36
print(f"{name} is {age} years old.")
print(f"Next year {name} will be {age + 1}.")
print(f"Shout: {name.upper()}!")
Output
Ada is 36 years old.
Next year Ada will be 37.
Shout: ADA!

Add a format spec after a colon to control how the value is displayed:

Python
price = 1234.5
ratio = 0.875
n = 42

print(f"{price:.2f}")       # 2 decimal places
print(f"{price:,.2f}")      # thousands separator
print(f"{ratio:.1%}")       # as a percentage
print(f"{n:05d}")           # pad with zeros to width 5
print(f"{n:b} {n:x}")       # binary and hex
print(f"[{'left':<10}]")    # align left in 10 chars
print(f"[{'right':>10}]")   # align right
print(f"[{'mid':^10}]")     # centre
print(f"[{'mid':*^10}]")    # centre, fill with *
Output
1234.50
1,234.50
87.5%
00042
101010 2a
[left      ]
[     right]
[   mid    ]
[***mid****]

Aligned columns make tidy reports:

Python
items = [("Keyboard", 2499), ("Mouse", 799), ("USB-C cable", 349)]
for item, price in items:
    print(f"{item:<12} ₹{price:>6,}")
Output
Keyboard     ₹ 2,499
Mouse        ₹   799
USB-C cable  ₹   349

Two more tricks. The = specifier prints the expression and its value — perfect for debugging — and !r shows the repr() (with quotes):

Python
user = "ada"
total = 3 * 7
print(f"{user = }")
print(f"{total = }, {user!r}")
Output
user = 'ada'
total = 21, 'ada'

Since Python 3.12, f-strings can even reuse the same quote type inside the braces (f"{", ".join(names)}"), although mixing quote styles remains more readable.

You may still see older styles in existing code: "Hi %s" % name and "Hi {}".format(name). They work, but prefer f-strings in new code.

Unicode: text is more than ASCII#

Python 3 strings are Unicode, so any language and emoji just works:

Python
greeting = "नमस्ते 🐘"
print(greeting, len(greeting))
print(ord("A"), chr(8377))
Output
नमस्ते 🐘 8
65 ₹

len counts code points, which is why the Hindi word counts as 6 (some visible letters are made of two code points). When you read and write files, always say encoding="utf-8" — you'll see that in the files lesson.

Worked example: a username generator#

Python
full_name = "  Grace Brewster Hopper "
year = 1906

parts = full_name.strip().lower().split()
username = parts[0] + parts[-1][0] + str(year)[-2:]
initials = "".join(p[0].upper() for p in parts)

print(f"Username: {username}")
print(f"Initials: {initials}")
print(f"Display:  {full_name.strip():^30}|")
Output
Username: graceh06
Initials: GBH
Display:      Grace Brewster Hopper     |

Common mistakes#

  • Forgetting the f: "Hi {name}" prints the braces literally.
  • Discarding method results: s.strip() alone does nothing useful; write s = s.strip().
  • Concatenating non-strings: "Total: " + 5 fails; use an f-string.
  • Building big strings with += in a loop: collect pieces in a list and "".join(pieces) at the end — it's clearer and faster.
  • Off-by-one slices: remember the stop index is excluded.

What's next#

Now that you can shape text, let's make programs interactive: the next lesson covers input and output — reading from the keyboard, printing nicely and handling command-line arguments.

Check your understanding

Quick quiz

0/3 answered
  1. 1.Given s = "Elephantoo", what is s[1:4]?

  2. 2.What happens when you run name = "ada" then name.upper() and print name?

  3. 3.Which f-string prints Total: 42.50 (the number right-aligned in 8 characters with 2 decimals)?

Finished reading?

Mark this lesson complete to track your progress.