Overview
Control flow determines the order in which code executes. Python provides powerful, readable constructs for branching, looping, and logical operations.
Conditional Statements
Basic If Statements
PYTHON
# Simple if
age = 18
if age >= 18:
print("You can vote")
# if-else
temperature = 25
if temperature > 30:
print("It's hot")
else:
print("It's comfortable")
# if-elif-else chain
score = 85
if score >= 90:
grade = 'A'
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
elif score >= 60:
grade = 'D'
else:
grade = 'F'
Ternary Operator
PYTHON
# Basic ternary
age = 20
status = "adult" if age >= 18 else "minor"
# Nested ternary (use sparingly)
score = 75
result = "excellent" if score >= 90 else "good" if score >= 70 else "needs improvement"
# With functions
def get_discount(is_member):
return 0.2 if is_member else 0.1
price = 100
final_price = price * (1 - get_discount(True))
Match Statement (Python 3.10+)
PYTHON
# Pattern matching
def process_command(command):
match command.split():
case ["quit"]:
return "Exiting..."
case ["hello", name]:
return f"Hello, {name}!"
case ["add", x, y]:
return int(x) + int(y)
case ["multiply", *numbers]:
result = 1
for n in numbers:
result *= int(n)
return result
case _:
return "Unknown command"
# Match with guards
def categorize_number(n):
match n:
case 0:
return "zero"
case n if n > 0:
return "positive"
case n if n < 0:
return "negative"
# Match with types
def process_data(data):
match data:
case int(x) if x > 0:
return f"Positive integer: {x}"
case float(x):
return f"Float value: {x}"
case str(s) if len(s) > 0:
return f"Non-empty string: {s}"
case list(items) if len(items) > 0:
return f"List with {len(items)} items"
case _:
return "Unhandled type"
Loops
For Loops
PYTHON
# Basic iteration
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
print(fruit)
# Range iteration
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# Range with start, stop, step
for i in range(2, 10, 2):
print(i) # 2, 4, 6, 8
# Enumerate for index and value
for index, value in enumerate(fruits):
print(f"{index}: {value}")
# Zip for parallel iteration
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Dictionary iteration
person = {'name': 'John', 'age': 30, 'city': 'NYC'}
for key, value in person.items():
print(f"{key}: {value}")
While Loops
PYTHON
# Basic while
counter = 0
while counter < 5:
print(counter)
counter += 1
# While with break
while True:
user_input = input("Enter 'quit' to exit: ")
if user_input == 'quit':
break
print(f"You entered: {user_input}")
# While with continue
count = 0
while count < 10:
count += 1
if count % 2 == 0:
continue # Skip even numbers
print(count)
Loop Control Statements
PYTHON
# Break - exit loop early
for i in range(10):
if i == 5:
break
print(i) # Prints 0-4
# Continue - skip current iteration
for i in range(5):
if i == 2:
continue
print(i) # Prints 0, 1, 3, 4
# Pass - placeholder
for i in range(3):
if i == 1:
pass # Do nothing, placeholder for future code
else:
print(i)
# Else clause (executes if loop completes without break)
for i in range(5):
if i == 10: # Never true
break
else:
print("Loop completed normally")
# While-else
n = 2
while n < 10:
if n % 7 == 0:
print(f"Found multiple of 7: {n}")
break
n += 1
else:
print("No multiple of 7 found")
Logical Operators
Boolean Operations
PYTHON
# Basic operators
x, y = True, False
print(x and y) # False
print(x or y) # True
print(not x) # False
# Short-circuit evaluation
def expensive_check():
print("Performing expensive check...")
return True
# 'and' short-circuits on False
result = False and expensive_check() # expensive_check() not called
# 'or' short-circuits on True
result = True or expensive_check() # expensive_check() not called
# Chaining comparisons
age = 25
is_valid = 18 <= age <= 65 # Equivalent to: 18 <= age and age <= 65
# Truthy and Falsy values
# Falsy: False, None, 0, 0.0, '', [], {}, set()
# Truthy: Everything else
# Using truthiness in conditions
data = []
if not data:
print("Data is empty")
name = input("Enter name: ") or "Anonymous" # Default value pattern
Comparison Operators
PYTHON
# Standard comparisons
a, b = 10, 20
print(a == b) # False (equality)
print(a != b) # True (inequality)
print(a < b) # True (less than)
print(a <= b) # True (less than or equal)
print(a > b) # False (greater than)
print(a >= b) # False (greater than or equal)
# Identity operators
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1
print(list1 == list2) # True (same values)
print(list1 is list2) # False (different objects)
print(list1 is list3) # True (same object)
# Membership operators
fruits = ['apple', 'banana', 'cherry']
print('banana' in fruits) # True
print('grape' not in fruits) # True
# String comparisons
print('apple' < 'banana') # True (lexicographical)
print('Apple' < 'apple') # True (capital letters come first)
Advanced Control Flow Patterns
Guard Clauses
PYTHON
def process_order(order):
# Early returns (guard clauses) for cleaner code
if order is None:
return "No order provided"
if order.status == 'cancelled':
return "Order is cancelled"
if order.total <= 0:
return "Invalid order total"
# Main logic here
return f"Processing order #{order.id}"
State Machines
PYTHON
class TrafficLight:
def __init__(self):
self.state = 'red'
def next_state(self):
transitions = {
'red': 'green',
'green': 'yellow',
'yellow': 'red'
}
self.state = transitions[self.state]
return self.state
# Usage
light = TrafficLight()
for _ in range(6):
print(f"Light is {light.state}")
light.next_state()
Nested Loops with Control
PYTHON
# Finding pairs with specific sum
def find_pairs_with_sum(numbers, target):
pairs = []
n = len(numbers)
for i in range(n):
for j in range(i + 1, n):
if numbers[i] + numbers[j] == target:
pairs.append((numbers[i], numbers[j]))
break # Break inner loop only
return pairs
# Matrix traversal with conditions
def process_matrix(matrix):
for i, row in enumerate(matrix):
for j, value in enumerate(row):
if value == 0:
continue # Skip zeros
if value < 0:
print(f"Negative at ({i},{j}): {value}")
break # Stop processing this row
print(f"Processing ({i},{j}): {value}")
Real-World Examples
Example 1: User Authentication System
PYTHON
class AuthenticationSystem:
def __init__(self):
self.users = {}
self.max_attempts = 3
self.locked_accounts = set()
def authenticate(self, username, password):
# Check if account is locked
if username in self.locked_accounts:
return "Account locked. Contact support."
# Check if user exists
if username not in self.users:
return "Invalid credentials"
user = self.users[username]
# Check password
if user['password'] != password:
user['attempts'] = user.get('attempts', 0) + 1
if user['attempts'] >= self.max_attempts:
self.locked_accounts.add(username)
return "Account locked due to multiple failed attempts"
remaining = self.max_attempts - user['attempts']
return f"Invalid credentials. {remaining} attempts remaining"
# Reset attempts on successful login
user['attempts'] = 0
# Check additional conditions
if user.get('requires_2fa'):
return "2FA required"
if user.get('password_expired'):
return "Password expired. Please reset."
return "Login successful"
Example 2: Game Loop with State Management
PYTHON
import random
class NumberGuessingGame:
def __init__(self, min_num=1, max_num=100):
self.min_num = min_num
self.max_num = max_num
self.reset_game()
def reset_game(self):
self.target = random.randint(self.min_num, self.max_num)
self.attempts = 0
self.max_attempts = 10
self.game_over = False
self.won = False
def play(self):
print(f"Guess a number between {self.min_num} and {self.max_num}")
while not self.game_over:
# Get user input
try:
guess = int(input(f"Attempt {self.attempts + 1}/{self.max_attempts}: "))
except ValueError:
print("Please enter a valid number")
continue
self.attempts += 1
# Check guess
if guess == self.target:
self.won = True
self.game_over = True
print(f"Congratulations! You won in {self.attempts} attempts!")
elif guess < self.target:
print("Too low!")
else:
print("Too high!")
# Check if max attempts reached
if self.attempts >= self.max_attempts:
self.game_over = True
if not self.won:
print(f"Game over! The number was {self.target}")
# Give hints
if not self.game_over and self.attempts == self.max_attempts - 2:
if self.target % 2 == 0:
print("Hint: The number is even")
else:
print("Hint: The number is odd")
# Ask to play again
play_again = input("Play again? (y/n): ").lower()
if play_again == 'y':
self.reset_game()
self.play()
# Usage
game = NumberGuessingGame()
# game.play()
Example 3: Data Validation Pipeline
PYTHON
class DataValidator:
def __init__(self):
self.errors = []
self.warnings = []
def validate_email(self, email):
if not email:
self.errors.append("Email is required")
return False
if '@' not in email:
self.errors.append("Invalid email format")
return False
domain = email.split('@')[1]
if '.' not in domain:
self.errors.append("Invalid email domain")
return False
return True
def validate_age(self, age):
try:
age_int = int(age)
except (ValueError, TypeError):
self.errors.append("Age must be a number")
return False
if age_int < 0:
self.errors.append("Age cannot be negative")
return False
elif age_int < 13:
self.warnings.append("User is under 13")
elif age_int > 120:
self.warnings.append("Unusual age value")
return True
def validate_password(self, password):
if len(password) < 8:
self.errors.append("Password must be at least 8 characters")
return False
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_digit = any(c.isdigit() for c in password)
has_special = any(c in '!@#$%^&*' for c in password)
if not all([has_upper, has_lower, has_digit]):
self.errors.append("Password must contain uppercase, lowercase, and digits")
return False
if not has_special:
self.warnings.append("Consider adding special characters for stronger password")
return True
def validate_user(self, user_data):
self.errors = []
self.warnings = []
# Validate each field
email_valid = self.validate_email(user_data.get('email'))
age_valid = self.validate_age(user_data.get('age'))
password_valid = self.validate_password(user_data.get('password'))
# Overall validation result
is_valid = email_valid and age_valid and password_valid
return {
'valid': is_valid,
'errors': self.errors,
'warnings': self.warnings
}
# Usage
validator = DataValidator()
user = {
'email': 'user@example.com',
'age': '25',
'password': 'SecurePass123'
}
result = validator.validate_user(user)
print(f"Valid: {result['valid']}")
if result['errors']:
print(f"Errors: {result['errors']}")
if result['warnings']:
print(f"Warnings: {result['warnings']}")
Practice Exercises
Exercise 1: Smart Menu System
PYTHON
# TODO: Create an interactive menu system
def create_menu_system():
"""
Create a menu system that:
- Displays options
- Handles invalid input
- Supports submenus
- Remembers user choices
- Provides help text
"""
# Your code here
pass
# Test your implementation
# menu = create_menu_system()
# menu.run()
Exercise 2: Pattern Detector
PYTHON
# TODO: Implement pattern detection in sequences
def detect_patterns(sequence):
"""
Detect patterns in a sequence:
- Arithmetic progression
- Geometric progression
- Fibonacci-like
- Palindrome
- Repeating pattern
Return pattern type and next predicted values
"""
# Your code here
pass
# Test cases
test_sequences = [
[2, 4, 6, 8], # Arithmetic
[1, 2, 4, 8], # Geometric
[1, 1, 2, 3, 5], # Fibonacci
[1, 2, 3, 2, 1], # Palindrome
[1, 2, 1, 2, 1, 2] # Repeating
]
Exercise 3: Advanced Calculator
PYTHON
# TODO: Build a calculator with control flow
class SmartCalculator:
"""
Implement a calculator that:
- Handles multiple operations
- Supports operation history
- Includes memory functions
- Validates input
- Provides undo functionality
"""
def __init__(self):
# Your initialization here
pass
def calculate(self, expression):
# Your code here
pass
def undo(self):
# Your code here
pass
# Usage example
# calc = SmartCalculator()
# result = calc.calculate("2 + 3 * 4")
Best Practices
1. Use guard clauses for early returns to reduce nesting 2. Prefer elif over nested if for readability 3. Use match/case for complex branching (Python 3.10+) 4. Avoid deep nesting - consider refactoring 5. Use enumerate() instead of range(len()) for iteration 6. Leverage else clauses in loops for completion detection 7. Short-circuit evaluation for performance 8. Meaningful variable names in loops 9. Break complex conditions into variables 10. Document complex logic with comments
Common Pitfalls
PYTHON
# Pitfall 1: Modifying list while iterating
numbers = [1, 2, 3, 4, 5]
# Wrong:
for n in numbers:
if n % 2 == 0:
numbers.remove(n) # Causes issues
# Correct:
numbers = [n for n in numbers if n % 2 != 0]
# Pitfall 2: Mutable default in conditions
def process_items(items, processed_list=[]): # Bad!
for item in items:
if item not in processed_list:
processed_list.append(item)
return processed_list
# Correct:
def process_items(items, processed_list=None):
if processed_list is None:
processed_list = []
# ... rest of function
# Pitfall 3: Infinite loops
# Wrong:
i = 0
while i < 10:
print(i)
# Forgot to increment i!
# Correct:
i = 0
while i < 10:
print(i)
i += 1
Performance Tips
- Use
setfor membership testing in loops - Prefer list comprehensions over loops for simple transformations
- Use generators for large sequences
- Cache expensive computations outside loops
- Profile code to identify bottlenecks
- Consider
itertoolsfor efficient iteration patterns