Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
16 minLesson 4 of 34
Python Foundations

Control Flow: if, elif, else

Control Flow: if, elif, else

Control flow is what makes programs intelligent. Without it, code just executes line by line. With it, your program can make decisions, react to different inputs, and branch into completely different paths.

Basic if/elif/else

score = 78

if score >= 90:
    print("Grade: A")
elif score >= 80:
    print("Grade: B")
elif score >= 70:
    print("Grade: C")
elif score >= 60:
    print("Grade: D")
else:
    print("Grade: F")

# Output: Grade: C

Python uses indentation (4 spaces) to define blocks — no curly braces. This is not optional formatting; it's part of the syntax.

Truthiness: What Evaluates to False?

In Python, many values besides False are "falsy":

# These all evaluate to False:
bool(0)         # False
bool(0.0)       # False
bool("")        # False — empty string
bool([])        # False — empty list
bool({})        # False — empty dict
bool(None)      # False

# These all evaluate to True:
bool(1)         # True
bool(-1)        # True — any non-zero number
bool("hello")   # True
bool([0])       # True — non-empty list (even if it contains 0)

This means you can write very Pythonic conditions:

name = input("Enter your name: ")

# Instead of: if name != "":
if name:
    print(f"Hello, {name}!")
else:
    print("No name provided.")

# Instead of: if len(items) > 0:
items = []
if items:
    print(f"Processing {len(items)} items")
else:
    print("Nothing to process")

Comparison and Logical Operators

# Comparison
x = 10
print(x > 5)    # True
print(x == 10)  # True
print(x != 5)   # True
print(x >= 10)  # True

# Logical operators
age = 25
has_id = True

if age >= 18 and has_id:
    print("Entry allowed")

if age < 18 or not has_id:
    print("Entry denied")

# Chaining comparisons (Python-specific, very readable)
temperature = 22
if 18 <= temperature <= 26:
    print("Comfortable temperature")

The Ternary Operator

Python's one-line conditional:

# Syntax: value_if_true if condition else value_if_false
age = 20
status = "adult" if age >= 18 else "minor"
print(status)  # "adult"

# Another example
score = 75
result = "pass" if score >= 60 else "fail"

Use it sparingly — for simple cases only. If the logic is complex, use a regular if/else block for readability.

Nested if Statements

def classify_number(n):
    if n > 0:
        if n % 2 == 0:
            return "positive even"
        else:
            return "positive odd"
    elif n < 0:
        return "negative"
    else:
        return "zero"

print(classify_number(4))   # positive even
print(classify_number(-3))  # negative
print(classify_number(0))   # zero

Avoid deep nesting — more than 2-3 levels makes code hard to read. Use early returns instead:

# Deeply nested — hard to read
def process_order(user, order):
    if user is not None:
        if user.is_active:
            if order is not None:
                if order.total > 0:
                    # actual logic...
                    return "processed"

# Better: early returns (guard clauses)
def process_order(user, order):
    if user is None:
        return "error: no user"
    if not user.is_active:
        return "error: inactive user"
    if order is None:
        return "error: no order"
    if order.total <= 0:
        return "error: invalid total"
    # actual logic with no nesting
    return "processed"

Python 3.10+ Match/Case Statement

Python's match/case is similar to switch statements in other languages but more powerful:

command = "quit"

match command:
    case "quit":
        print("Exiting...")
    case "help":
        print("Commands: quit, help, start")
    case "start":
        print("Starting application...")
    case _:               # _ is the default/wildcard
        print(f"Unknown command: {command}")

Match with patterns:

point = (0, 1)

match point:
    case (0, 0):
        print("Origin")
    case (x, 0):
        print(f"On x-axis at {x}")
    case (0, y):
        print(f"On y-axis at {y}")
    case (x, y):
        print(f"Point at ({x}, {y})")

Real-World Example: Login System

def authenticate(username, password, users_db):
    """Authenticate a user and return their role."""
    
    # Guard clauses first
    if not username or not password:
        return None, "Username and password required"
    
    if username not in users_db:
        return None, "Invalid credentials"  # Don't say "user not found" — security
    
    user = users_db[username]
    
    if not user['active']:
        return None, "Account is disabled"
    
    if user['password'] != hash_password(password):
        return None, "Invalid credentials"
    
    return user['role'], "Login successful"


users_db = {
    "alice": {"password": "hashed_pw_1", "role": "admin", "active": True},
    "bob":   {"password": "hashed_pw_2", "role": "user",  "active": True},
    "carol": {"password": "hashed_pw_3", "role": "user",  "active": False},
}

role, message = authenticate("alice", "correctpassword", users_db)
print(f"Role: {role}, Message: {message}")

Key Principles

  • Use if name: not if name != "" — Pythonic truthiness checks are cleaner
  • Use early returns to avoid deep nesting
  • Prefer elif over nested if when conditions are mutually exclusive
  • Ternary for simple one-liners, regular if/else for anything else

Next lesson: Loops — mastering for, while, and Python's powerful comprehensions.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!