Chapter 2: Control Flow - Making Decisions Like a Pythonista¶
You know how to make decisions in code. if this, else that. But Python does it differently, and once you see why, you'll appreciate the elegance.
Indentation: It's Not Optional, It's the Syntax¶
First things first. Python doesn't use curly braces or end keywords. Your indentation is your code structure.
That indentation? It's not for humans. It's the actual syntax. Mix tabs and spaces? Python will yell at you. Inconsistent indentation? Error. This seems strict until you realize you'll never have a debate about where to put braces or whether to use them for single-line blocks.
Use 4 spaces. Not tabs, not 2 spaces. Four spaces is the Python way.
The if-elif-else Dance¶
You've written this a million times, but here's the Python version:
age = 25
if age < 13:
print("child")
elif age < 20:
print("teenager")
elif age < 60:
print("adult")
else:
print("senior")
Notice elif, not else if. Python likes brevity when it doesn't hurt clarity. No parentheses around conditions either (you can add them, but why?).
Truthiness: What Counts as True?¶
Here's where Python gets interesting. Everything has a boolean value in a conditional context:
if "hello": # non-empty string? True
if "": # empty string? False
if [1, 2]: # non-empty list? True
if []: # empty list? False
if 42: # non-zero number? True
if 0: # zero? False
if None: # None? Always False
This is called "truthiness." Empty containers are falsy. Zero is falsy. None is falsy. Everything else is usually truthy.
This lets you write clean code:
Shorter, cleaner, more Pythonic. But be careful - this can bite you when 0 is a valid value:
user_input = 0
if user_input: # Oops! 0 is falsy, this block won't run
print(f"You entered: {user_input}")
When zero matters, be explicit: if user_input is not None:.
The Ternary Operator: One-Liner if-else¶
Python has a ternary operator, but it reads differently than C-style languages:
# Other languages: condition ? value_if_true : value_if_false
# Python: value_if_true if condition else value_if_false
status = "even" if x % 2 == 0 else "odd"
It reads almost like English: "status is 'even' if x is divisible by 2, else 'odd'." Use it for simple assignments. Don't nest them - that way lies madness.
Match-Case: Python's Modern Switch¶
From Python 3.10 onwards, you've got match-case. It's not just a switch statement - it's pattern matching:
def describe_number(n):
match n:
case 0:
return "zero"
case 1 | 2 | 3: # multiple values with |
return "small"
case n if n < 0: # with guard condition
return "negative"
case _: # default case
return "other"
The _ is the wildcard - catches everything else. But here's where it gets powerful:
point = (0, 5)
match point:
case (0, 0):
print("origin")
case (0, y): # matches any point on y-axis, captures y value
print(f"on y-axis at {y}")
case (x, 0):
print(f"on x-axis at {x}")
case (x, y):
print(f"somewhere at ({x}, {y})")
Pattern matching with destructuring. If you're on Python 3.10+, use it. If not, stick with if-elif-else.
Loops: The for and while Story¶
The for Loop: It's Always a For-Each¶
Python's for loop is different. There's no C-style for(i=0; i<10; i++). Every for loop iterates over a collection:
# Looping over a list
for item in [1, 2, 3]:
print(item)
# Looping over a string
for char in "hello":
print(char)
# The classic counter pattern
for i in range(5): # 0, 1, 2, 3, 4
print(i)
range() gives you a sequence of numbers. range(5) is 0 to 4. range(1, 10) is 1 to 9. range(0, 10, 2) is 0, 2, 4, 6, 8.
Want both index and value? Use enumerate():
This is cleaner than maintaining a counter manually.
The while Loop: Same Old Friend¶
While loops work like everywhere else:
Nothing fancy. When you need to loop until a condition changes, use while. When you're iterating over something, use for.
Break, Continue, and the Else Clause Nobody Uses¶
break exits the loop. continue skips to the next iteration. You know this.
But here's Python's quirk - loops can have an else clause:
The else runs only if the loop completes normally (no break). It's useful for search operations:
for user in users:
if user.name == search_name:
print(f"Found: {user}")
break
else:
print("User not found")
Most Python developers don't use this often. But when you need it, it's elegant.
Pass: The Art of Doing Nothing¶
Sometimes you need a placeholder. Python doesn't allow empty blocks, so you use pass:
It's literally a no-op. The code equivalent of "noted" in a conversation.
What You Just Learned:
- Indentation is syntax, not style
- Everything has truthiness (empty = false, non-empty = true)
- elif, not else if
- Ternary reads like English
- match-case for pattern matching (Python 3.10+)
- for loops always iterate over collections
- Loops can have else clauses
- pass when you need to do nothing
Next: we'll dive into lists, tuples, and the art of storing multiple things without losing your mind.