Warming up the neural circuits...
By the end of this chapter you will:
Most real programs are text in, numbers out. You read a CSV file (text), parse the values (strings), convert them to numbers, do math, and format the results back into text. Master strings and numbers, and you can handle 90% of everyday programming tasks.
This chapter covers the string and number operations you'll use daily. By the end, you'll be fluent in slicing, formatting, and math — and you'll know why 0.1 + 0.2 doesn't equal 0.3.
Strings are sequences of characters. Click each category to see the methods you'll use daily:
text = "Hello, World!"
print(text.upper()) # 'HELLO, WORLD!'
print(text.lower()) # 'hello, world!'
print(text.title()) # 'Hello, World!'
print(text.capitalize()) # 'Hello, world!'
print(text.swapcase()) # 'hELLO, wORLD!'| Method | Returns | Use when |
|---|---|---|
upper() | ALL CAPS | Comparing case-insensitively |
lower() | all lowercase | Normalizing |
title() | Capitalize Each Word | Display names |
capitalize() | First char uppercase | Sentence case |
swapcase() | Swap case | Rarely used |
text = " hello "
print(text.strip()) # 'hello' (both sides)
print(text.lstrip()) # 'hello ' (left side)
print(text.rstrip()) # ' hello' (right side)| Method | Returns | Use when |
|---|---|---|
strip() | Remove both sides |
text = "Hello, World!"
print(text.find("World")) # 7 (index, -1 if not found)
print(text.index("World")) # 7 (raises ValueError if not found)
print(text.count("l
text = "apple,banana,cherry"
parts = text.split(",") # ['apple', 'banana', 'cherry']
print("|".join(parts)) # 'apple|banana|cherry'
sentence = "Hello world"
words = sentence.filename = "report.pdf"
print(filename.startswith("report")) # True
print(filename.endswith(".pdf")) # True
text = "Hello123"
print(text
Think of string operations as a pipeline:
strip(), lower()split(), find()replace(), upper()join(), f-stringsStrings are sequences of characters. You can access individual characters using indexing and extract substrings using slicing.
name = "Alice"
print(name[0]) # 'A' (first character)
print(name[4]) # 'e' (fifth character)
print(name[-1]) # 'e' (last character)
print(name[-Key facts:
0, not 1.-1 is the last character, -2 is the second-to-last.IndexError: string index out of range.Slicing extracts a substring: string[start:stop:step].
name = "Alice"
print(name[0:3]) # 'Ali' (characters 0, 1, 2 — stop is exclusive)
print(name[1:4]) # 'lic'
print(name[:3]) # 'Ali' (start defaults to 0)
print
Key facts:
start is inclusive, stop is exclusive.start to start from the beginning; omit stop to go to the end.step controls the stride: 2 means every other character, -1 means reverse.Mental model: Think of slicing as "from index start, up to but not including stop, stepping by step."
f-strings (formatted string literals) let you embed expressions inside strings. They're the modern, readable way to format output.
name = "Alice"
age = 30
print(f"Name: {name}, Age: {age}")
# Name: Alice, Age: 30You can put any Python expression inside {}:
x = 10
y = 20
print(f"Sum: {x + y}") # Sum: 30
print(f"Upper: {'hello'.upper()}") # Upper: HELLOControl how values are formatted with : followed by a format specifier:
price = 19.99
print(f"Price: ${price:.2f}") # Price: $19.99 (2 decimal places)
pi = 3.14159
print(f"Pi: {pi:.3f}") # Pi: 3.142 (3 decimal places, rounded)
number = 42
print(f
name = "Alice"
print(f"{name:<10}|") # 'Alice |' (left-aligned, 10 chars)
print(f"{name:>10}|") # ' Alice|' (right-aligned, 10 chars)
print(f"{name:^10}|"The = specifier prints the expression and its value — perfect for debugging:
x = 10
y = 20
print(f"{x + y = }") # x + y = 30
print(f"{x = }") # x = 10Strings have dozens of methods. Here are the ones you'll use every day.
text = "Hello, World!"
print(text.upper()) # 'HELLO, WORLD!'
print(text.lower()) # 'hello, world!'
print(text.title()) # 'Hello, World!'
print(text.capitalize())
text = " hello "
print(text.strip()) # 'hello' (both sides)
print(text.lstrip()) # 'hello ' (left side)
print(text.rstrip()) # ' hello' (right side)text = "Hello, World!"
print(text.find("World")) # 7 (index of first occurrence, -1 if not found)
print(text.index("World")) # 7 (same as find, but raises ValueError if not found)
print(text.count("l"
text = "apple,banana,cherry"
parts = text.split(",") # ['apple', 'banana', 'cherry']
print("|".join(parts)) # 'apple|banana|cherry'
# Split on whitespace (default)
sentence = "Hello world"
filename = "report.pdf"
print(filename.startswith("report")) # True
print(filename.endswith(".pdf")) # Truetext = "Hello123"
print(text.isalpha()) # False (contains digits)
print(text.isdigit()) # False (contains letters)
print(text.isalnum()) # True (letters and digits only)
print(text.isspacea = 10
b = 3
print(a + b) # 13 (addition)
print(a - b) # 7 (subtraction)
print(a * b) # 30 (multiplication)
print(a / b
Key facts:
/ always returns a float, even if the result is a whole number: 10 / 2 → 5.0.// returns an int if both operands are ints: 10 // 3 → 3.% is useful for checking divisibility: n % 2 == 0 means n is even.x = 3.14
y = 2.0
print(x + y) # 5.14
print(x * y) # 6.28
print(x / y) # 1.57Floats are approximations. Some decimal numbers can't be represented exactly in binary:
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # FalseWhy? 0.1 and 0.2 are repeating fractions in binary (like 1/3 in decimal). When you add them, the tiny rounding errors accumulate.
How to handle it:
# Use round() for display
print(round(0.1 + 0.2, 1)) # 0.3
# Use math.isclose() for comparisons
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
#
Python integers have arbitrary precision — they can be as large as memory allows:
big = 10 ** 100 # 1 followed by 100 zeros
print(big) # Works fine, no overflowUse underscores for readability:
population = 8_000_000_000 # 8 billionParsing CSV data:
row = "Alice,30,1000.50"
parts = row.split(",")
name = parts[0] # 'Alice'
age = int(parts[1]) # 30
balance = float(parts[
Formatting reports:
items = [
("Apple", 1.50, 3),
("Banana", 0.75, 6),
("Cherry", 2.00, 2),
]
print(f"{'Item':<10} {'Price
String cleaning:
user_input = " Hello, World! "
cleaned = user_input.strip().lower()
print(cleaned) # 'hello, world!'| Mistake | Why it's wrong | Fix |
|---|---|---|
text[10] → IndexError: string index out of range | The string is shorter than 11 characters. | Check the length first: if len(text) > 10: |
text[1:10] doesn't raise an error even if the string is short | Slicing is forgiving — it returns as much as it can. | This is usually what you want, but be aware it won't tell you if the string is too short. |
"5" + 3 → TypeError | You can't add a string and an int. | Convert: int("5") + 3 or f"{5 + 3}" |
0.1 + 0.2 == 0.3 → False | Floating-point precision errors. | Use math.isclose() or round() for comparisons. |
10 / 3 → 3.333... when you wanted |
String immutability: Strings are immutable — every operation that "modifies" a string actually creates a new one. For heavy string manipulation (e.g., building a large string in a loop), use "".join(list_of_strings) instead of repeated concatenation.
Unicode: Python 3 strings are Unicode by default. You can store emojis, accented characters, and non-Latin scripts without issues:
emoji = "🎉"
accented = "café"
chinese = "你好"Performance: String operations are fast for small strings. For large-scale text processing (e.g., parsing gigabyte files), consider using re (regular expressions) or external libraries like pandas.
text = "Python Programming", extract: first 6 chars, last 11 chars, every other char, reversed.email = " USER@Example.COM ", convert to lowercase and strip whitespace.0.1 + 0.2 == 0.3 evaluate to? How do you fix it?"Alice,30,Engineer" into name, age (int), and occupation.Beginner:
"What's the difference between text[1:5] and text[1:5:2]?"
text[1:5]extracts characters from index 1 to 4 (stop is exclusive).text[1:5:2]does the same but steps by 2, so it extracts characters at indices 1 and 3.
"Why does 0.1 + 0.2 == 0.3 return False?"
Floats are approximations in binary.
0.1and0.2can't be represented exactly, so their sum has a tiny rounding error. Usemath.isclose()orround()for comparisons.
Senior:
"What's the difference between and in Python?"
text[0], slice with text[1:5:2].f"Name: {name}, Price: ${price:.2f}".upper(), strip(), split(), join(), replace(), startswith()./ is true division (returns float), // is floor division (returns int).math.isclose() or Decimal for exact arithmetic.I want to…
├── Get a character → text[0]
├── Extract a substring → text[1:5]
├── Reverse a string → text[::-1]
├── Format output → f"Value: {x:.2f}"
├── Convert case → text.upper(), text.lower()
├── Remove whitespace → text.strip()
├── Split a string → text.split(",")
├── Join strings → ",".join(parts)
├── Divide integers → 10 // 3 (floor), 10 / 3 (true)
└── Compare floats → math.isclose(a, b)What does text[1:5:2] do for text='Python'?
| General cleaning |
lstrip() | Remove left | Leading whitespace |
rstrip() | Remove right | Trailing whitespace |
Common pattern: user_input.strip().lower() — clean and normalize.
| Method | Returns | Use when |
|---|---|---|
find(sub) | Index or -1 | Safe search |
index(sub) | Index or ValueError | When missing is an error |
count(sub) | Number of occurrences | Counting |
replace(old, new) | New string | Substituting text |
Warning: find() returns -1 if not found. index() raises ValueError.
| Method | Returns | Use when |
|---|---|---|
split(sep) | List of strings | Parsing CSV, splitting words |
join(list) | Single string | Building strings from lists |
Common pattern: " ".join(words) — join with space.
| Method | Returns | Use when |
|---|---|---|
startswith(prefix) | True/False | Checking file types |
endswith(suffix) | True/False | Checking extensions |
isalpha() | True/False | Validating names |
isdigit() | True/False | Validating numbers |
3/ does true division (returns float). |
Use // for floor division: 10 // 3 → 3 |
text.replace("old", "new") doesn't change text | Strings are immutable. replace() returns a new string. | text = text.replace("old", "new") |
///
/does true division and always returns a float:10 / 3→3.333....//does floor division and returns an int (if both operands are ints):10 // 3→3. It rounds down to the nearest integer.
"Why are strings immutable in Python? What are the implications?"
Strings are immutable for safety (they can be used as dictionary keys), performance (Python can optimize by caching strings), and thread safety. The implication is that every "modification" creates a new string. For heavy manipulation, use
"".join()instead of repeated concatenation.