Nested Conditionals in Python

Some decisions in code genuinely depend on another decision already being true. python nested if statements are how you express that — an if living inside another if, checking a second condition that only makes sense once the first has already passed. This article covers how nesting works, when it's the right tool, and — just as important — when it isn't.

What Are Nested Conditionals?

A nested conditional is simply an if statement placed inside another if (or else) block.

number = 25

if number > 10:
    if number > 20:
        print("Greater than 20")

The inner if number > 20: only gets evaluated at all if the outer condition, number > 10, was already True. Nested conditionals exist for exactly this situation: a second check that genuinely only makes sense after a first one has already been satisfied.

How Nesting Works: Indentation and Flow

Each level adds indentation

As covered in the earlier article on Python syntax, each nested block requires its own additional level of indentation — the deeper the nesting, the further right the code shifts:

number = 25

if number > 10:          # first level
    if number > 20:        # second level
        print("Greater than 20")
The outer condition acts as a gatekeeper

This is the core idea to hold onto: the outer if controls whether the inner one even gets a chance to run. If the outer condition is False, Python never evaluates the inner block at all — it's skipped entirely, exactly the same way a single unmatched if gets skipped.

Practical example: age and license status
age = 20
has_license = True

if age >= 18:
    if has_license:
        print("You can drive")
    else:
        print("You need a license first")
else:
    print("You're too young to drive")

Notice the structure here: the license check only happens inside the branch where age is already confirmed to be 18 or older. There's no reason to even ask about a license if the person is underage — the outer condition correctly gatekeeps the inner one.

3. Nested if-else and Multiple Levels

Combining if-else at each level

You can pair if/else at every level of nesting, not just the outer one, to express more complete branching logic:

age = 16
is_member = True

if age >= 18:
    if is_member:
        price = 8
    else:
        price = 12
else:
    if is_member:
        price = 5
    else:
        price = 7

print(price)   # 5

This handles all four combinations of age and membership status, with each branch reachable only through its specific combination of outer and inner conditions.

Nesting elif chains inside an outer if

You can also nest a full elif chain inside an outer if, useful when an entire category of logic should only run under some outer gatekeeping condition:

score = 78
attempted = True

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

print(grade)   # C

Here, the entire grading elif chain only matters if the outer attempted check passed — otherwise, none of the score thresholds are even relevant.

How deep nesting can go — and why it usually shouldn't

Technically, Python doesn't impose any real practical limit on how many levels deep you can nest conditionals. Four, five, six levels — it'll run. But each additional level makes the code measurably harder to read and mentally trace, and it's rarely necessary. Section 5 covers concrete ways to avoid it.

Nested Conditionals vs. Chained Conditionals (elif)

The key distinction

This is the single most useful thing to internalize from this article: elif chains are for mutually exclusive, either/or scenarios — exactly one branch should run, and the branches represent alternative possibilities for the same question. Nesting is for conditions that genuinely depend on one another — the inner question doesn't even make sense to ask unless the outer one was already answered a certain way.

Side-by-side example
# elif — mutually exclusive alternatives answering the same question ("what grade?")
score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"
# Nesting — the inner question depends on the outer one
age = 20
has_license = True

if age >= 18:            # first question: are they even old enough?
    if has_license:         # second question: only relevant if they are
        print("Can drive")

Trying to force the license example into an elif chain wouldn't make sense, because "has a license" and "is under 18" aren't alternative answers to the same question — they're two separate, dependent facts. Conversely, forcing the grading example into nested if statements would work but add unnecessary indentation for what's really a single flat decision.

Common mistake: nesting where and/or or elif would be clearer

A frequent beginner habit is reaching for nested if statements when a combined condition using and or or — covered in the earlier logical operators article — would say the same thing more directly:

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

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

Both versions behave identically. The flattened version is simply easier to read at a glance, since there's no need to mentally track two separate indentation levels to understand that both conditions are required.

Best Practices for Readable Nested Code

Guideline: avoid nesting more than 2–3 levels deep

As a practical rule of thumb, if you find yourself nesting more than two or three levels deep, treat that as a signal to refactor rather than push forward. Deeply nested code becomes genuinely hard to follow — by the time you're four levels in, tracking which combination of outer conditions got you there requires real mental effort from anyone reading it.

Alternative: combining conditions with and/or

As shown above, this is the simplest fix when nested conditions don't actually depend on separate, distinct questions — they're really just several requirements for the same single decision.

Alternative: early returns and guard clauses

Inside a function, one of the most effective ways to flatten nested conditionals is the "guard clause" pattern: check for disqualifying conditions early, return immediately if they apply, and let the rest of the function assume the happy path.

# Before: deeply nested
def check_eligibility(age, has_license, has_insurance):
    if age >= 18:
        if has_license:
            if has_insurance:
                return "Eligible to drive"
            else:
                return "Needs insurance"
        else:
            return "Needs a license"
    else:
        return "Too young"
# After: flattened with guard clauses
def check_eligibility(age, has_license, has_insurance):
    if age < 18:
        return "Too young"
    if not has_license:
        return "Needs a license"
    if not has_insurance:
        return "Needs insurance"
    return "Eligible to drive"

Both versions produce identical results, but the second one never nests more than one level deep. Each guard clause handles exactly one disqualifying condition and exits immediately, so by the time you reach the final return, every prerequisite has already been confirmed — there's no need to keep tracking multiple layers of "but only if the outer condition was also true."

This pattern is worth adopting early. It won't fit every situation — some logic genuinely is hierarchical, like the age/license/membership pricing example earlier — but for the common case of "check several disqualifying conditions before proceeding," guard clauses consistently produce flatter, more readable code than deep nesting.