Python If Else

The if statement is how a Python program makes decisions. It lets your code branch: run one block of statements when a condition is true, and optionally a different block when it isn’t. Almost every non-trivial program — from form validation to game logic to data pipelines — relies on conditional branching, so understanding exactly how Python evaluates conditions and chooses a branch is essential before you move on to loops, functions, or anything else.

Overview: How Conditional Branching Works

An if statement evaluates an expression and converts the result to a boolean using Python’s truthiness rules. If the result is True, the indented block directly under the if runs. If it’s False, Python checks the next elif (short for “else if”), if one exists, and repeats the process. If none of the if/elif conditions are true, the optional else block runs. You can chain as many elif clauses as you like, but there is at most one if and at most one final else.

Python doesn’t use curly braces to mark blocks the way C, Java, or JavaScript do. Instead, it uses indentation — consistently 4 spaces per level by convention. Every statement indented under an if, elif, or else header (which must end with a colon :) is considered part of that block. When the interpreter parses your file, it tracks indentation levels and emits INDENT/DEDENT tokens internally; a block ends the moment the indentation decreases back to the level of the header.

Internally, when Python compiles an if statement to bytecode, the condition expression is evaluated first and its result is passed through bool(). Python then emits a conditional jump instruction (roughly POP_JUMP_IF_FALSE) that skips the body if the condition is falsy. This is why any Python object, not just True/False, can be used directly as a condition — Python calls the object’s __bool__ method (or falls back to __len__, treating a length of 0 as false) to decide.

Truthy and Falsy Values

Every object in Python has an implicit boolean value. The following are considered falsy: False, None, 0, 0.0, 0j, empty strings "", and empty containers like [], (), {}, and set(). Everything else — including any non-empty string (even "False"!), any non-zero number, and any non-empty container — is truthy. This means you can write if my_list: instead of if len(my_list) > 0:, which is both shorter and considered more Pythonic.

Syntax

if condition_1:
    # runs if condition_1 is truthy
    statement(s)
elif condition_2:
    # runs if condition_1 was falsy and condition_2 is truthy
    statement(s)
elif condition_3:
    # you can chain as many elif clauses as needed
    statement(s)
else:
    # runs only if every condition above was falsy
    statement(s)
Part Required? Purpose
if condition: Yes (exactly one) The first condition checked. Must end with a colon.
elif condition: No (any number) Checked only if all prior conditions were falsy.
else: No (at most one, must be last) Runs when no condition above was true; takes no condition of its own.
Indented block Yes, under each header One or more statements executed for that branch.

Python also supports a compact conditional expression (often called a “ternary”), useful for simple value selection:

value = expression_if_true if condition else expression_if_false

Examples

Example 1: Grading with if / elif / else

score = 82

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

print(f"Score: {score} -> Grade: {grade}")

Output:

Score: 82 -> Grade: B

Python checks each condition top to bottom. score >= 90 is false (82 is not ≥ 90), so it moves to the next elif. score >= 80 is true, so grade is set to "B" and Python skips every remaining branch — it never even evaluates the score >= 70 check. Only one branch of an if/elif/else chain ever runs.

Example 2: Nested conditions and logical operators

username = "admin"
password = "secret123"
is_active = True

if username == "admin" and password == "secret123":
    if is_active:
        print("Access granted.")
    else:
        print("Account is disabled.")
else:
    print("Invalid credentials.")

Output:

Access granted.

The outer if uses and to require both the username and password checks to be true; and short-circuits, so if username == "admin" were false, Python would never bother evaluating the password comparison at all. Inside that branch, a second, nested if/else checks a separate condition (is_active). Nesting lets you express “this decision only matters once we already know that” — but as shown in Best Practices below, deep nesting should be used sparingly.

Example 3: Truthy/falsy values and a ternary expression

cart_items = []

message = "Cart has items" if cart_items else "Cart is empty"
print(message)

cart_items.append("Book")

if cart_items and "Book" in cart_items:
    print(f"You have {len(cart_items)} item(s), including a Book.")
else:
    print("No book in cart.")

Output:

Cart is empty
You have 1 item(s), including a Book.

The first line uses a ternary expression: since cart_items starts as an empty list, it’s falsy, so message becomes "Cart is empty" without needing a full four-line if/else block. After appending "Book", cart_items becomes truthy (non-empty), and the and combined with the in membership operator confirms the list is both non-empty and contains "Book".

Under the Hood: Step by Step

  1. Python evaluates the condition expression next to if (e.g. score >= 90) to produce some object.
  2. That object is passed through the equivalent of bool(obj). If the type defines __bool__, that method is called; otherwise Python tries __len__ (0 means falsy); if neither exists, the object is always truthy.
  3. If the result is True, the interpreter executes the indented block and then jumps to the statement after the entire if/elif/else chain — it does not fall through to check other branches.
  4. If the result is False, the interpreter moves to the next elif (if any) and repeats steps 1–3.
  5. If every if/elif condition was falsy and an else exists, its block runs unconditionally.
  6. If nothing matched and there is no else, execution simply continues after the chain — doing nothing is a perfectly valid outcome.

Common Mistakes

Mistake 1: Using = instead of ==

Beginners coming from other habits sometimes try to assign inside a condition:

x = 5
if x = 5:
    print("five")

This is not valid Python and raises a SyntaxError immediately — = is the assignment operator and cannot appear inside an expression like this (Python only allows assignment-like behavior inside an expression via the explicit walrus operator :=, in specific contexts). Use the equality operator == to compare values:

x = 5
if x == 5:
    print("five")

Mistake 2: Using is when you mean ==

a = [1, 2, 3]
b = [1, 2, 3]

if a is b:
    print("Same list")
else:
    print("Different objects")

Output:

Different objects

This surprises many newcomers: a and b contain the exact same values, so it feels like they should be “equal”. But is checks object identity (are these the same object in memory?), not value equality. a and b are two distinct list objects that happen to hold equal contents, so is returns False. To compare values, use == instead, which would correctly print "Equal contents" for these two lists. Reserve is for identity checks such as if value is None:, where comparing against the singleton None object by identity is the idiomatic and recommended approach.

Best Practices

  • Rely on truthiness: prefer if my_list: over if len(my_list) > 0:, and if not my_list: over if len(my_list) == 0:.
  • Never write if flag == True: or if flag == False: — just write if flag: or if not flag:.
  • Use is / is not only for identity checks, especially against singletons like None (e.g. if result is None:), and == / != for comparing values.
  • Avoid deeply nested if blocks where possible; consider combining conditions with and/or, or returning early from a function to reduce nesting (a pattern often called “guard clauses”).
  • Use chained comparisons where they read naturally, e.g. if 0 <= age < 18: instead of if age >= 0 and age < 18:.
  • Keep condition expressions readable; if a condition grows long or complex, assign it to a well-named variable first, e.g. is_eligible = age >= 18 and has_id, then write if is_eligible:.
  • For dispatching on many discrete, literal values of a single variable, consider Python’s match/case statement (Python 3.10+) instead of a long elif chain.
  • Always use consistent 4-space indentation (per PEP 8) and never mix tabs and spaces — mixing them causes a TabError or, worse, silently misaligned blocks.

Practice Exercises

  1. Write a program that stores a temperature in Celsius in a variable. Using if/elif/else, print "Freezing" if it’s at or below 0, "Cold" if it’s between 1 and 15 (inclusive), "Mild" if between 16 and 25 (inclusive), and "Hot" otherwise.
  2. Write a program that takes three numbers stored in variables and prints the largest of the three, using only if/elif/else comparisons (no built-in max()).
  3. Write a ternary expression that assigns the string "even" or "odd" to a variable parity based on whether an integer n is divisible by 2, then print parity.

Summary

  • if, elif, and else let a program branch based on conditions, and only one branch in a chain ever executes.
  • Python uses indentation, not braces, to define blocks — be consistent with 4-space indentation.
  • Any value can be used as a condition thanks to truthiness rules: empty/zero-like values are falsy, everything else is truthy.
  • Use ==/!= to compare values and reserve is/is not for identity checks like is None.
  • Ternary expressions (x if cond else y) offer a concise alternative for simple value selection.
  • Favor readable, shallow conditionals over deep nesting; combine conditions with and/or or use guard clauses.