Python · lesson 7 of 34
Beginner
16- 1 Introduction to Python
- 2 Setting Up Python
- 3 Python Variables and Type System
- 4 Python Data Types
- 5 Python Operators
- 6 Python Strings
- 7 Control Flow in Python
- 8 Python Functions and Parameters
- 9 Python Data Structures
- 10 Python Collections
- 11 Python Modules and Packages
- 12 Object-Oriented Programming in Python
- 13 Encapsulation and Name Mangling in Python
- 14 Polymorphism and Duck Typing in Python
- 15 Abstraction with Abstract Base Classes in Python
- 21 Building a Python CLI Tool
Intermediate
12- 16 Multiple Inheritance and MRO in Python
- 17 Composition vs Inheritance in Python
- 18 Python Dataclasses
- 19 Error Handling in Python
- 20 File I/O, pathlib, and JSON in Python
- 22 Python Decorators and Closures
- 23 Python Generators
- 24 Python Context Managers
- 25 Python Concurrency
- 27 Testing Python Code
- 28 Python Security
- 34 Python Interview Prep
Advanced
6Control Flow in Python
Run if/elif/else, match-case, for and while loops, for/else, break/continue and comprehensions in Python, each example shown with its real output.
By SkillByExample · Updated
Output below is from Python 3.13.
if / elif / else
Conditional statements let your program make decisions based on runtime values. Python uses indentation (4 spaces by convention) to delimit blocks instead of braces, and there are no parentheses required around the condition.
# grade.py
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B" # this branch runs because 85 >= 80
elif score >= 70:
grade = "C"
else:
grade = "F"
print(grade)
$ python grade.py
B
Inline Conditional (Ternary)
For simple two-branch conditions, Python’s ternary expression keeps the logic on one readable line. Reserve it for simple cases — nesting ternaries hurts readability fast.
# ternary.py
age = 20
status = "adult" if age >= 18 else "minor"
print(status)
age = 12
status = "adult" if age >= 18 else "minor"
print(status)
$ python ternary.py
adult
minor
Truthiness Shortcuts
Python evaluates any object in a boolean context, not just explicit True/False. Leveraging this makes conditions more concise and idiomatic — the second form below is considered un-Pythonic.
# truthiness.py
items = []
if not items:
print("Empty (first)")
if len(items) == 0:
print("Empty (second)")
$ python truthiness.py
Empty (first)
Empty (second)
match-case (Python 3.10+)
match-case is structural pattern matching — a significant upgrade over a traditional value-switch. It can destructure sequences, match object attributes, and bind variables in a single step, which eliminates the nested if/elif chains that were previously needed for command parsing or protocol handling.
# match_case.py
def handle_command(command):
match command.split():
case ["quit"]:
return "Exiting."
case ["go", direction]:
# 'direction' is bound to the second word automatically
return f"Going {direction}"
case ["pick", "up", item]:
return f"Picked up {item}"
case ["drop", *items]:
# *items captures all remaining words into a list
return f"Dropped: {', '.join(items)}"
case _:
# _ is the wildcard — matches anything not caught above
return f"Unknown command: {command}"
print(handle_command("go north"))
print(handle_command("pick up sword"))
print(handle_command("drop key map"))
$ python match_case.py
Going north
Picked up sword
Dropped: key, map
Matching on Types and Attributes
match-case can match against class types and bind specific attribute values in the same case clause — something that would require multiple isinstance calls and attribute accesses with traditional if/elif.
# match_dataclass.py
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def classify(point):
match point:
case Point(x=0, y=0):
return "Origin"
case Point(x=0, y=y):
return f"Y-axis at {y}"
case Point(x=x, y=0):
return f"X-axis at {x}"
case Point(x=x, y=y):
return f"Point at ({x}, {y})"
print(classify(Point(0, 0)))
print(classify(Point(0, 5)))
print(classify(Point(3, 4)))
$ python match_dataclass.py
Origin
Y-axis at 5
Point at (3, 4)
The x: float annotation is not enforced — Point(0, 5) stores the int 5, and that is what the pattern binds and prints. Class patterns compare with ==, so Point(0.0, 0.0) would match Point(x=0, y=0) too.
for Loops
Python’s for loop iterates over any iterable — lists, strings, ranges, generators, files, dictionaries, and more. There is no C-style for (i=0; i<n; i++) in Python; if you need an index, use enumerate().
# loops.py
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
print("---")
for i in range(5): # 0, 1, 2, 3, 4
print(i)
print("---")
for i in range(2, 10, 2): # 2, 4, 6, 8 (start, stop, step)
print(i)
$ python loops.py
apple
banana
cherry
---
0
1
2
3
4
---
2
4
6
8
enumerate() and zip()
enumerate() and zip() solve two of the most common looping patterns — getting an index alongside the value, and iterating two sequences together — without manually managing index variables.
# enumerate_zip.py
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
print("---")
names = ["Alice", "Bob"]
scores = [95, 87]
for name, score in zip(names, scores):
print(f"{name}: {score}")
$ python enumerate_zip.py
1. apple
2. banana
3. cherry
---
Alice: 95
Bob: 87
for / else
The else clause on a for loop runs when the loop completes normally — that is, without hitting a break. This is useful for “search and report not found” patterns without needing a sentinel variable.
# for_else.py
def find_prime(numbers):
for n in numbers:
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
break # composite — inner loop exits early
else:
# else runs only when the inner loop finished without break
return n # found a prime
return None
print(find_prime([4, 6, 8, 9, 10, 13, 15]))
$ python for_else.py
13
while Loops
while loops repeat as long as their condition is truthy. They’re the right tool when you don’t know in advance how many iterations you need — waiting for user input, reading until EOF, or polling until a condition is met.
# while_loops.py
count = 0
while count < 5:
print(count)
count += 1
$ python while_loops.py
0
1
2
3
4
Walrus Operator in while
The walrus operator (:=) is particularly useful in while loops — it reads a value, assigns it, and checks it in a single expression, eliminating the boilerplate of reading before the loop and again at the top of each iteration.
# walrus.py
import io
data = io.StringIO("line1\nline2\nline3\n")
while line := data.readline():
print(f"Got: {line.strip()}")
$ python walrus.py
Got: line1
Got: line2
Got: line3
break and continue
break and continue give you fine-grained control over loop execution. They’re most useful when a condition inside the loop body is easier to express than rewriting the loop condition itself.
# break_continue.py
for n in range(10):
if n == 5:
break
print(n)
print("---")
for n in range(10):
if n % 2 == 0:
continue # skip even numbers
print(n) # 1, 3, 5, 7, 9
$ python break_continue.py
0
1
2
3
4
---
1
3
5
7
9
Comprehensions
Comprehensions provide a concise syntax for constructing new collections from existing iterables. They’re not just syntactic sugar — Python compiles them to optimized bytecode that’s typically faster than equivalent for loops with .append() calls.
List Comprehension
# list_comprehensions.py
squares = [x**2 for x in range(10)]
print(squares)
evens = [x**2 for x in range(1, 11) if x % 2 == 0]
print(evens)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [cell for row in matrix for cell in row]
print(flat)
$ python list_comprehensions.py
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[4, 16, 36, 64, 100]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Dict Comprehension
# dict_comprehensions.py
words = ["hello", "world", "python"]
word_lengths = {word: len(word) for word in words}
print(word_lengths)
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
print(inverted)
$ python dict_comprehensions.py
{'hello': 5, 'world': 5, 'python': 6}
{1: 'a', 2: 'b', 3: 'c'}
Set Comprehension
# set_comprehension.py
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x**2 for x in numbers}
print(unique_squares)
$ python set_comprehension.py
{16, 1, 4, 9}
The duplicates collapsed to four squares, but notice the printed order: a set has no order of its own, so it lists elements in hash-table slot order, which is why 16 can come out first.
Generator Expression
A generator expression looks like a list comprehension but produces values lazily — one at a time, on demand. Use it when you only need to iterate once or when the full collection would use too much memory.
# generators.py
list_total = sum([x**2 for x in range(1_000_000)])
gen_total = sum(x**2 for x in range(1_000_000))
print(list_total == gen_total)
$ python generators.py
True
Both produce the same sum — the list comprehension builds a million-element list in memory first, while the generator expression streams values one at a time. For sum(), the generator is more memory-efficient and just as fast.
Common Patterns
Flat map
# flat_map.py
sentences = ["hello world", "foo bar baz"]
words = [word for sentence in sentences for word in sentence.split()]
print(words)
$ python flat_map.py
['hello', 'world', 'foo', 'bar', 'baz']
Conditional expression in comprehension
# abs_values.py
values = [1, -2, 3, -4, 5]
abs_values = [x if x >= 0 else -x for x in values]
print(abs_values)
$ python abs_values.py
[1, 2, 3, 4, 5]
Early exit with next()
next() with a generator expression efficiently finds the first match in a sequence without scanning the whole thing. The second argument is the default if nothing matches.
# early_exit.py
users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
alice = next((u for u in users if u["name"] == "Alice"), None)
print(alice)
nobody = next((u for u in users if u["name"] == "Nobody"), None)
print(nobody)
$ python early_exit.py
{'id': 1, 'name': 'Alice'}
None
Practice
1. Prime number finder: Write a function that finds all prime numbers up to a given limit using a for...else loop.
Solution
def primes_up_to(limit):
primes = []
for n in range(2, limit + 1):
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
break
else:
primes.append(n)
return primes
print(primes_up_to(20))
$ python primes.py
[2, 3, 5, 7, 11, 13, 17, 19]
The else clause runs when the inner loop completes without break — meaning no divisor was found, so n is prime.
2. Match-case command parser: Extend the handle_command function to also handle a "help" command that returns a list of available commands.
Solution
def handle_command(command):
match command.split():
case ["quit"]:
return "Exiting."
case ["help"]:
return "Commands: quit, go, pick, drop"
case ["go", direction]:
return f"Going {direction}"
case ["pick", "up", item]:
return f"Picked up {item}"
case ["drop", *items]:
return f"Dropped: {', '.join(items)}"
case _:
return f"Unknown command: {command}"
print(handle_command("help"))
$ python help_command.py
Commands: quit, go, pick, drop
3. Comprehension with filter: Use a list comprehension with a filter to find all words longer than 4 characters in a sentence.
Solution
sentence = "The quick brown fox jumps over the lazy dog"
long_words = [word for word in sentence.split() if len(word) > 4]
print(long_words)
$ python long_words.py
['quick', 'brown', 'jumps']
The comprehension filters the split result, keeping only words where len(word) > 4. With > 5 the result would be [] — no word in this sentence has six letters.