BASIC Python (Beginner Level) 📂 Basic Python Programming · 5 of 15 43 min read

Python Control Flow — if, elif, else, Truthiness, Logical Operators & Match Statement

Master Python control flow from the ground up. This tutorial covers the if statement with animated flowcharts, if/else two-path decisions, if/elif/else cascades, complete truthiness rules (what counts as True and False), comparison operators with chained comparisons, logical operators (and, or, not) with short-circuit evaluation, nested if vs guard clauses, ternary expressions, the modern match statement, common traps, and a full smart-grader practice program.

Section 01

The Story That Explains Control Flow

The Fork in the Road
You're driving to a friend's house and hit a fork in the road. A sign says "If it's raining, take the left road; otherwise, take the right." You glance at the sky, make a decision, and continue. That single moment — look at a condition, choose a path, keep going — is exactly what control flow is in programming.

Without it, a program would be a single straight line: instruction 1, instruction 2, instruction 3, done. Nothing interesting. Real programs need to decide — should I send this email or wait? Is the user logged in or a guest? Is this number positive, negative, or zero? Every decision is a fork, and every fork is written with if, elif, and else.

Master this trio and you master the shape of every program you'll ever write. Everything else — loops, functions, classes — is just organizing decisions. Control flow is where a dumb list of instructions becomes something that thinks.
if
The condition
Ask a yes/no question. If the answer is True, run the code block underneath. If False, skip it. Every control-flow story starts with an if.
🔁
elif
The next question
Short for "else if". Ask a follow-up question, but only if the previous one was False. You can chain as many elif branches as you need to make a decision cascade.
📐
else
The fallback
Catch-all. Runs only if every preceding if and elif was False. Optional, but often used as the "default case" of the whole decision.

Section 02

The Simple if Statement

The simplest control-flow structure is a single if. You write a condition; if it's True, the indented block runs. If it's False, the block is skipped and the program continues below.

start age >= 18? (condition) True print("Adult") False continue...
# ─── BASIC IF SYNTAX ────────────────────────────────────
age = 21

if age >= 18:
    print("You can vote.")
    print("You can sign a contract.")

print("Program continues here.")

# OUTPUT:
# You can vote.
# You can sign a contract.
# Program continues here.

The Anatomy of an if Statement

Four Required Parts
1The keyword if
2A condition — any expression that evaluates to True or False
3A colon : at the end of the line
4An indented block of code (4 spaces by convention)
⚠️
Indentation Is Syntax, Not Just Style

Unlike C or Java, Python uses indentation to define code blocks — not braces. Everything indented under an if belongs to it. Break the indentation and the meaning changes. Use 4 spaces (never mix tabs and spaces), and let your editor auto-indent for you.


Section 03

Two Paths — if / else

Real decisions almost always have a "what if not?" branch. That's what else is for. Exactly one of the two blocks will run — never both, never neither.

start score >= 60? True print("Pass") False print("Fail") exactly ONE ran
# ─── if-else — EXACTLY ONE BRANCH RUNS ──────────────────
score = 75

if score >= 60:
    print("Pass")
else:
    print("Fail")

# OUTPUT: Pass

# ─── COMMON PATTERN: DEFAULTING VALUES ─────────────────
name = input("Your name: ")
if name:
    print(f"Hello, {name}!")
else:
    print("Hello, stranger!")

Section 04

Many Paths — if / elif / else

Life rarely fits into two categories. A test score isn't just "pass or fail" — it's A, B, C, D, or F. That's where elif shines: a chain of decisions evaluated in order, top to bottom. Python checks each condition; the first one that's True runs its block, and Python skips the rest.

start score >= 90? True grade = "A" False score >= 80? True grade = "B" False score >= 70? True grade = "C" False (else) grade = "F" continue
# ─── GRADE CLASSIFIER ───────────────────────────────────
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"

print(f"Score: {score} → Grade: {grade}")
# OUTPUT: Score: 85 → Grade: B
🔑
Order Matters — Check the Most Specific First

Python evaluates conditions top to bottom and stops at the first True. In the grade example, score = 95 matches score >= 90 first, so it gets "A" and Python never checks score >= 80. If you accidentally reversed the order (checked 60 first), everyone passing would get "D". Rule of thumb: put stricter conditions higher.


Section 05

Truthiness — What Counts as True in Python?

The condition after if doesn't have to be an obvious True or False. Python quietly converts any value to a boolean using rules called truthiness. This lets you write elegant checks like if name: or if items: without ever spelling out the comparison.

❌ Falsy Values (evaluate as False)
ValueType
Falsebool
NoneNoneType
0, 0.0, 0jnumeric zero
""empty string
[]empty list
()empty tuple
{}empty dict
set()empty set
✅ Truthy Values (evaluate as True)
ValueWhy
Truethe constant itself
1, -1, 42, 3.14any nonzero number
"hello"non-empty string
" "even a space is non-empty!
[0]list with any items
{"a": 1}non-empty dict
(0,)tuple with any items
Basically: anything not in the falsy list
# ─── ELEGANT TRUTHINESS CHECKS ──────────────────────────
name = ""
if name:                            # "" is falsy → skip
    print(f"Hi, {name}")
else:
    print("No name given")         # runs

items = []
if items:                           # empty list → False → skip
    print("Processing items...")
else:
    print("Nothing to do.")         # runs

# ─── VERIFY WITH bool() ────────────────────────────────
print(bool(0))                # False
print(bool(""))               # False
print(bool([]))               # False
print(bool(None))             # False

print(bool(-1))               # True — nonzero
print(bool("False"))          # True — the STRING is non-empty!
print(bool(" "))              # True — one space is non-empty!
print(bool([0]))              # True — list has an item (even if the item is 0)
⚠️
The Sneakiest Truthiness Bug

bool("False") is True — because "False" is a five-character string, not the boolean. If you read user input and check if answer:, typing anything at all counts as True, even the word "no". Convert explicitly: if answer.lower() == "yes":.


Section 06

Building Conditions — Comparison Operators

The heart of any if is a condition, and conditions are built from comparison operators. They compare two values and return True or False.

OperatorMeaningExampleResult
==Equal to5 == 5True
!=Not equal to5 != 3True
>Greater than7 > 3True
<Less than7 < 3False
>=Greater or equal5 >= 5True
<=Less or equal4 <= 5True
inMembership3 in [1, 2, 3]True
not inNon-membership"x" not in "abc"True
isIdentity (same object)a is Noneuse for None/True/False

Chained Comparisons — Python's Sweet Trick

# ─── PYTHON LETS YOU CHAIN COMPARISONS ──────────────────
age = 25

# Instead of writing this:
if age >= 18 and age <= 65:
    print("Working age")

# You can write this — cleaner and more mathematical:
if 18 <= age <= 65:
    print("Working age")          # same result!

# Chains work with any operators
if 0 < x < 100:                    # between 0 and 100 (exclusive)
    print("in range")

# ─── USE == NOT = FOR COMPARISONS ──────────────────────
# if age = 18:      → SyntaxError!
# if age == 18:     → correct

# ─── DON'T CONFUSE == AND is ───────────────────────────
# Use == to compare VALUES
print("abc" == "abc")             # True — same value

# Use 'is' ONLY for None, True, False
value = None
if value is None:              # CORRECT idiom
    print("no value")

Section 07

Combining Conditions — and, or, not

Real-world decisions rarely depend on a single condition. Python's three logical operators let you combine them: and (both must be True), or (at least one must be True), and not (flip True↔False).

and — ALL must be True
A and B is True only when both A and B are True. If A is False, Python doesn't even check B — this is called short-circuit evaluation. Use for "requires everything" conditions.
Both required
🔄
or — ANY must be True
A or B is True when either A or B is True. If A is True, Python skips checking B. Use for "any of these will do" conditions like weekend or holiday.
Either works
not — flip the result
not A gives the opposite: True becomes False, False becomes True. Handy for readability: if not user.active: reads better than if user.active == False:.
Negation
# ─── and ────────────────────────────────────────────────
age = 25
has_license = True

if age >= 18 and has_license:
    print("Can drive")

# ─── or ─────────────────────────────────────────────────
day = "Saturday"
is_holiday = False

if day == "Saturday" or day == "Sunday" or is_holiday:
    print("Day off!")

# CLEANER using 'in':
if day in ("Saturday", "Sunday") or is_holiday:
    print("Day off!")

# ─── not ────────────────────────────────────────────────
logged_in = False

if not logged_in:
    print("Please log in")

# ─── COMBINING ALL THREE ────────────────────────────────
if (age >= 18 and age < 65) or not is_holiday:
    print("Working hours apply")

Short-Circuit Evaluation — A Free Feature

# ─── PYTHON STOPS EVALUATING AT THE FIRST DECISIVE VALUE ─

# and — if the first is False, the second is NEVER checked
user = None
if user and user.name == "Alice":  # SAFE — user.name never accessed if user is None
    print("Hi Alice")

# or — if the first is True, the second is NEVER checked
name = ""
display = name or "Anonymous"       # "" is falsy → use default
print(display)                        # Anonymous

# This makes 'or' great for default values
port = config.get("port") or 8080     # fallback if missing
💡
Operator Precedence Reminder

When mixing operators: comparisons happen first, then not, then and, then or. When in doubt, use parentheses to make intent explicit — (a > 0 and b > 0) or c > 0 reads better than relying on precedence memory.


Section 08

Nested if — Decisions Inside Decisions

You can place an if inside another if. This is nesting, and it's useful when the second question only makes sense after the first has been answered.

# ─── NESTED IF — LOGIN + PERMISSIONS ────────────────────
logged_in = True
is_admin = False

if logged_in:
    print("Welcome back!")
    if is_admin:
        print("Admin panel available")
    else:
        print("Standard user view")
else:
    print("Please log in first")

# OUTPUT:
# Welcome back!
# Standard user view

When to Flatten Nested Conditions

Deep nesting is a code smell — it's hard to read and easy to get wrong. Often you can flatten nested conditions using and, or use guard clauses (early returns/exits) to reduce indentation.

❌ Deeply Nested (hard to read)
Code
if user:
    if user.active:
        if user.role == "admin":
            grant_access()
Three levels of indentation just to grant access.
✅ Flat With and (readable)
Code
if user and user.active and user.role == "admin":
    grant_access()
One level, one condition, obvious intent. Uses short-circuit safely.
# ─── GUARD CLAUSES — EARLY EXITS BEATS NESTING ──────────

# INSTEAD OF NESTING everything:
def process(order):
    if order:
        if order.paid:
            if order.items:
                ship(order)
            else:
                print("No items")
        else:
            print("Not paid")
    else:
        print("No order")

# USE GUARD CLAUSES to fail fast and stay flat:
def process(order):
    if not order:
        print("No order")
        return
    if not order.paid:
        print("Not paid")
        return
    if not order.items:
        print("No items")
        return
    ship(order)                    # the "happy path" is flat and clear

Section 09

The Ternary Expression — if in One Line

For simple two-way decisions, Python offers a compact conditional expression (often called the ternary operator). It fits on one line and returns a value, so you can use it in assignments and function arguments.

Ternary Syntax
value_if_true if condition else value_if_false
Reads like English: "give me A if condition, else B."
# ─── FULL if-else ───────────────────────────────────────
age = 20
if age >= 18:
    status = "adult"
else:
    status = "minor"

# ─── SAME THING AS A TERNARY ────────────────────────────
status = "adult" if age >= 18 else "minor"
print(status)                    # adult

# ─── INLINE INSIDE OTHER EXPRESSIONS ────────────────────
print(f"You are an {'adult' if age >= 18 else 'minor'}.")

# ─── SAFE DIVISION ──────────────────────────────────────
count = 0
avg = total / count if count > 0 else 0

# ─── DEFAULT PARAMETER PATTERN ─────────────────────────
def greet(name=None):
    display = name if name else "stranger"
    print(f"Hello, {display}")

greet()                            # Hello, stranger
greet("Alice")                     # Hello, Alice
⚠️
Ternaries Are for SIMPLE Decisions Only

Don't nest ternaries or chain them with more than one condition. If you find yourself writing a if x else b if y else c, stop and use a proper if-elif-else — it will be far easier to read a week from now. The ternary is for quick two-way choices; anything more deserves the full statement.


Section 10

The match Statement — Python's Modern Alternative

Since Python 3.10, there's a fourth control-flow tool called match. It's designed for cases where you compare one value against many possibilities — think of it as a supercharged switch statement. For simple value comparisons it's cleaner than a long elif chain, and it can also unpack complex data structures.

# ─── LONG elif CHAIN (works in every Python version) ────
def http_status(code):
    if code == 200:
        return "OK"
    elif code == 301:
        return "Moved"
    elif code == 404:
        return "Not Found"
    elif code == 500:
        return "Server Error"
    else:
        return "Unknown"

# ─── SAME THING WITH match (Python 3.10+) ──────────────
def http_status(code):
    match code:
        case 200:
            return "OK"
        case 301:
            return "Moved"
        case 404:
            return "Not Found"
        case 500:
            return "Server Error"
        case _:                          # the "default" case
            return "Unknown"

# ─── MATCH CAN GROUP CASES WITH | ──────────────────────
def day_kind(day):
    match day:
        case "Saturday" | "Sunday":
            return "weekend"
        case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
            return "weekday"
        case _:
            return "unknown"
💡
When to Use Which

Use if / elif / else for ranges and complex boolean logic (score >= 90, a and b). Use match when you're comparing one value against a fixed set of possibilities, or destructuring data structures. For older codebases (Python 3.9 and below), match isn't available — stick with elif.


Section 11

Common Traps and How to Avoid Them

Using = instead of ==
if age = 18: → SyntaxError. Assignment vs comparison. Python's SyntaxError catches this — but only in the check, not always in your logic. Always double-check: == compares, = assigns.
Missing the colon
if x > 0 (no colon!) → SyntaxError. Every if, elif, and else line ends with :. Watch the end of the line.
Inconsistent indentation
Mixing tabs and spaces or shifting alignment breaks the block boundaries. Use 4 spaces everywhere and configure your editor to show whitespace. This is the #1 confusing error for beginners.
Overlapping elif conditions
Writing if x > 60: ... elif x > 70: ... — the second branch never runs, because x > 60 catches everything the second would. Order strictest to loosest.
Comparing with == True or == False
if active == True: works but is verbose. Just write if active:. Similarly if not active: is cleaner than if active == False:.
Using 'is' for value comparison
if x is 100: may work today (due to Python's small-int cache) and mysteriously fail tomorrow. Use is only for None, True, False.

Section 12

Complete Practice Program — Smart Grader

# grader.py — every control-flow concept in one program

def letter_grade(score):
    """Convert numeric score to letter grade using elif chain."""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

def is_passing(score):
    """Ternary expression — one-line if/else."""
    return "Passed" if score >= 60 else "Failed"

def honors_status(score, attendance):
    """Combine multiple conditions with 'and'."""
    if score >= 95 and attendance >= 95:
        return "Highest Honors"
    elif score >= 90 and attendance >= 90:
        return "Honors"
    else:
        return "Regular"

def validate(score):
    """Guard clauses — early exits."""
    if not isinstance(score, (int, float)):
        return "Error: score must be a number"
    if score < 0 or score > 100:
        return "Error: score out of range"
    return None          # no error

def analyze(student):
    name, score, attendance = student
    line = "─" * 45
    print(f"{line}\n  {name}")

    # Guard clause — validate first
    error = validate(score)
    if error:
        print(f"  {error}")
        return

    grade = letter_grade(score)
    status = is_passing(score)
    honors = honors_status(score, attendance)

    # Chained comparison
    if 70 <= score <= 89:
        note = "Solid effort — room to grow"
    elif score >= 90:
        note = "Excellent work!"
    else:
        note = "Needs improvement"

    print(f"  Score:      {score}")
    print(f"  Grade:      {grade}")
    print(f"  Status:     {status}")
    print(f"  Attendance: {attendance}%")
    print(f"  Honors:     {honors}")
    print(f"  Note:       {note}")

def main():
    students = [
        ("Alice",   96, 98),
        ("Bob",     72, 85),
        ("Carol",   58, 70),
        ("Dan",     88, 92),
        ("Erin",    150, 95),   # invalid — guard catches this
    ]

    for student in students:
        analyze(student)

if __name__ == "__main__":
    main()
OUTPUT
───────────────────────────────────────────── Alice Score: 96 Grade: A Status: Passed Attendance: 98% Honors: Highest Honors Note: Excellent work! ───────────────────────────────────────────── Bob Score: 72 Grade: C Status: Passed Attendance: 85% Honors: Regular Note: Solid effort — room to grow ───────────────────────────────────────────── Carol Score: 58 Grade: F Status: Failed Attendance: 70% Honors: Regular Note: Needs improvement ───────────────────────────────────────────── Dan Score: 88 Grade: B Status: Passed Attendance: 92% Honors: Regular Note: Solid effort — room to grow ───────────────────────────────────────────── Erin Error: score out of range

Section 13

Golden Rules

🐍 Python Control Flow — Non-Negotiable Rules
1
Every if, elif, and else line ends with a colon. The block that follows is indented (4 spaces). Miss either and Python raises SyntaxError or IndentationError.
2
Exactly one branch of an if/elif/else chain runs. Python checks conditions top to bottom and stops at the first True. Order matters — put strictest conditions first.
3
Use == for comparison, = for assignment. Confusing them is the classic beginner error. is is reserved for None, True, and False — never for numbers or strings.
4
Prefer truthiness checks over explicit comparisons. if items: beats if len(items) > 0:. if not name: beats if name == "":. Cleaner, more Pythonic, less prone to bugs.
5
Chain comparisons for range checks. 18 <= age <= 65 is cleaner than age >= 18 and age <= 65 — same meaning, but reads like math.
6
Use guard clauses to reduce nesting. When multiple conditions must all be true, use early returns or continues to handle the failure cases first, leaving the happy path flat and readable.
7
Short-circuit evaluation is a feature, not a bug. user and user.name is safe — if user is None, .name is never accessed. Similarly, value or default is a common fallback idiom.
8
Reach for the ternary only for simple, obvious two-way choices. "adult" if age >= 18 else "minor" is fine; nested or chained ternaries are not. When in doubt, use a full if block — future you will thank present you.