AlgoInsight

Free lesson · Python Basic to Advance

`if`, `elif`, `else` Statements

Easy8 min readpython

if, elif, and else Statements

What if Statements Are

The if statement is Python's primary tool for conditional execution. Code inside an if block runs only when a condition is true. The elif and else keywords extend this to multiple alternatives.

Every Python program has conditional logic. if statements are how that logic is expressed.

Basic Syntax

The simplest form is if alone:

age = 20

if age >= 18:
    print("Adult")
# Adult
This code tests whether age is at least eighteen. The condition is True, so the body runs and prints Adult. If age were less than eighteen, nothing would print.

The colon marks the end of the condition. The body is indented. Indentation defines block membership (Topic 2.8).

Add else for the alternative:

age = 15

if age >= 18:
    print("Adult")
else:
    print("Minor")
# Minor
This code tests age against eighteen. With age fifteen, the condition is False, so the else branch runs and prints Minor.

For more than two cases, use elif:

score = 75

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"

print(grade)
# C
This code assigns a letter grade based on score thresholds. With score seventy-five, the first two conditions fail, the third condition score at least seventy succeeds, so grade is set to C and the remaining branches are skipped.

elif is short for "else if." Python evaluates conditions top to bottom and runs the first matching branch. Once a branch matches, the rest are skipped.

The Condition

The condition is any expression that evaluates to a boolean (or to a value that has a truth value). Comparison operators are common:

x = 10

if x > 5:
    print("greater than 5")
# greater than 5

if x == 10:
    print("exactly 10")
# exactly 10

if x != 0:
    print("not zero")
# not zero
This code shows three independent if statements with different comparisons. All three conditions are True for x equal to ten, so all three messages print.

Logical operators combine conditions:

age = 25
has_license = True

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

if age < 13 or age > 65:
    print("Special category")
# (nothing prints - neither condition is true)
This code combines conditions with and and or. The first if requires both age at least eighteen and has_license True, both of which are met. The second if requires age below thirteen or above sixty-five, neither of which is met, so nothing prints.

The full operator set is covered in Topic 2.5.

Truthiness

The condition doesn't have to be a boolean. Any value works - Python evaluates its truthiness:

items = []

if items:
    print("Has items")
else:
    print("Empty")
# Empty
This code uses an empty list as a condition. Empty containers are falsy in Python, so the else branch runs and prints Empty.

The truthiness rules from Topic 2.4:

This enables compact patterns:

name = "Alice"

if name:
    print(f"Hello, {name}")
# Hello, Alice
This code checks whether name is non-empty using its truthiness. The string Alice is truthy, so the f-string prints Hello Alice. An empty string would skip the branch.

This pattern is idiomatic but has a subtle trap. Sometimes you want to distinguish "missing" from "empty." For example, 0 and None are both falsy but mean different things:

count = 0

if count:
    print(f"Count is {count}")
else:
    print("No count")
# No count
This code tests count of zero. Zero is falsy, so the else branch runs, even though zero is a valid count value. To distinguish zero from missing, an explicit comparison is needed.

For "is this value present (even if zero or empty)?" use is not None:

count = 0

if count is not None:
    print(f"Count is {count}")
else:
    print("No count")
# Count is 0
This code uses is not None to test for presence rather than truthiness. Zero is not None, so the if branch runs and prints Count is zero. This pattern distinguishes a present zero from a missing value.

Interactive animation: The whole falsy list on one screen, then if items running against an empty list and printing empty.

Interactive animation: With count = 0, if count reports No count while if count is not None prints Count is 0.

Nested Conditionals

if statements can contain other if statements:

age = 25
income = 50000

if age >= 18:
    if income >= 30000:
        print("Eligible")
    else:
        print("Not eligible - income too low")
else:
    print("Not eligible - age too low")
# Eligible
This code nests an income check inside an age check. With age twenty-five and income fifty thousand, the outer if succeeds and the inner if succeeds, so Eligible prints. Each level of nesting adds four spaces of indentation.

Deep nesting hurts readability. The same logic with combined conditions:

age = 25
income = 50000

if age >= 18 and income >= 30000:
    print("Eligible")
elif age < 18:
    print("Not eligible - age too low")
else:
    print("Not eligible - income too low")
# Eligible
This code flattens the nested if into combined conditions. The first branch handles the eligible case; the elif handles the age failure; the else handles the income failure. The flat structure is usually easier to read than nesting.

When nesting goes more than two levels deep, consider extracting helper functions or using early returns (covered below).

Early Returns

Inside a function, an if followed by return can short-circuit further checks. This is called the "early return" or "guard clause" pattern:

def process_user(user):
    if user is None:
        return "no user"
    if not user.get("active"):
        return "inactive"
    if user.get("age", 0) < 18:
        return "minor"
    return f"processing {user['name']}"

print(process_user(None))
# no user
print(process_user({"active": False}))
# inactive
print(process_user({"active": True, "age": 15}))
# minor
print(process_user({"active": True, "age": 25, "name": "Alice"}))
# processing Alice
This code uses guard clauses to handle each invalid case at the top of the function. Each if checks a failure condition and returns immediately. The happy path appears at the bottom with no nesting. Four sample calls show each branch firing in turn.

Compare to the equivalent deeply-nested version:

def process_user(user):
    if user is not None:
        if user.get("active"):
            if user.get("age", 0) >= 18:
                return f"processing {user['name']}"
            else:
                return "minor"
        else:
            return "inactive"
    else:
        return "no user"
This code shows the deeply-nested alternative to guard clauses. Each successful check pushes the happy path further to the right. The actual processing call is buried three indentation levels deep. The guard-clause form is generally preferred.

Early returns keep the "main" code path at the leftmost indentation level. Failure cases handle themselves and exit.

Interactive animation: The same order check written as four nested ifs and as guard clauses that keep the real work unindented.

Conditional Expressions

For simple cases where a condition produces a value, the conditional expression (ternary) is more compact than an if/else block. This is covered in Topic 4.2.

For example, instead of:

age = 20
if age >= 18:
    status = "adult"
else:
    status = "minor"

print(status)
# adult
This code uses a four-line if-else to assign a status. With age twenty, status is set to adult. The same logic can be expressed in one line using a conditional expression, covered in the next topic.

You can write:

age = 20
status = "adult" if age >= 18 else "minor"
print(status)
# adult
This code uses a conditional expression to assign status in one line. The value adult is chosen because age is at least eighteen; otherwise minor would be chosen. Conditional expressions are best for short, value-producing branches.

Use conditional expressions for short value selections. Use if/else for statements with side effects or multi-line branches.

Common Patterns

Range Check

Check if a value is in a range using a chained comparison:

score = 75

if 0 <= score <= 100:
    print("Valid score")
# Valid score
This code uses a chained comparison to check that score is between zero and one hundred inclusive. Chained comparisons are clearer than the equivalent score greater-equal zero and score less-equal one hundred.

Interactive animation: A chained comparison checks both bounds at once, alongside short-circuit guards and in replacing a chain of ors.

Membership Check

Use in to check membership in a collection:

day = "Saturday"

if day in {"Saturday", "Sunday"}:
    print("Weekend")
else:
    print("Weekday")
# Weekend
This code uses in to check whether day is one of two weekend strings. Saturday is in the set, so Weekend prints. Using a set rather than a list makes the membership test O(1), though for two items the difference is negligible.

Dispatch by Value

For many discrete values, a dict can replace a long if/elif chain:

def get_discount(membership):
    discounts = {
        "gold": 0.20,
        "silver": 0.10,
        "bronze": 0.05,
    }
    return discounts.get(membership, 0.0)

print(get_discount("gold"))
# 0.2
print(get_discount("none"))
# 0.0
This code replaces an if-elif chain with a dictionary lookup. The discounts dict maps membership levels to discount fractions. get returns the discount if the membership is known, or zero point zero if not. This pattern scales well as more memberships are added.

Dict dispatch is concise and easy to extend. It works best when each case returns a value rather than executing different code.

For pattern matching with structured data, the match statement (Topic 4.8) is more powerful.

Interview Relevance

Interview: Conditional logic appears in nearly every algorithm interview. Edge cases are scored heavily: empty inputs, single elements, boundary values, negative inputs. Walk through edge cases explicitly during coding rounds.

Guard clauses and early returns are usually preferred over deep nesting. A function with three nested ifs signals a refactoring opportunity to many interviewers.

Common questions: "What's the difference between if x: and if x is not None:?" "When would you use elif instead of separate ifs?" "How would you refactor this nested if?"

Module 30 covers Python interview preparation.

Common Pitfalls

Warning: Using = instead of ==. if x = 5: is a SyntaxError in Python (unlike C). if x == 5: is the correct comparison.

Testing truthiness when distinguishing absence matters. if count: treats 0 and None the same. Use if count is not None: when zero is a valid value.

Forgetting elif when separate ifs aren't needed. Several independent if statements check every condition; elif short-circuits after the first match.

Deep nesting. More than two levels of nested if is hard to follow. Refactor with guard clauses or helper functions.

Comparing to True or False explicitly. if x == True: is less idiomatic than if x:. Reserve explicit equality for cases where you must distinguish True from other truthy values.

Mutable defaults in dict.get. discounts.get(key, []) returns the same list every call if combined with mutation. Use if key in dict for clarity when defaults are mutable.

Try It

Exercise: Build conditional fluency:

  1. Write a function that returns a letter grade for a numeric score, using elif.
  2. Refactor a three-level nested if into guard clauses with early returns.
  3. Write a function that determines whether a year is a leap year (divisible by 4, except for centuries that aren't divisible by 400).
  4. Demonstrate the difference between if value: and if value is not None: for the value 0.
  5. Replace a five-case if/elif chain with a dictionary lookup.
  6. Use a chained comparison to test whether a temperature is between -10 and 40 degrees Celsius.

Back to Python Basic to Advance