Every meaningful program needs to make decisions. Whether you are validating user input, routing requests, or handling edge cases, conditional statements are how your code responds to different situations at runtime. They are the foundation of control flow — the mechanism that determines which lines of code execute and in what order.
In Python, conditionals are clean, readable, and indentation-driven. There are no curly braces or explicit end keywords. If you are coming from Java, C++, or JavaScript, you will find Python’s approach refreshingly minimal. This tutorial walks through every conditional construct Python offers, from basic if statements to the modern match-case pattern matching introduced in Python 3.10.
Before writing conditionals, you need to understand the operators that produce True or False values. These are the building blocks of every condition you will ever write.
== — Equal to: checks if two values are the same!= — Not equal to: checks if two values differ< — Less than> — Greater than<= — Less than or equal to>= — Greater than or equal tox = 10 y = 20 print(x == y) # False print(x != y) # True print(x < y) # True print(x > y) # False print(x <= 10) # True print(x >= 15) # False
One critical distinction: == checks equality, while is checks identity (whether two variables point to the same object in memory). For value comparison, always use ==.
if StatementThe if statement is the most fundamental decision-making construct. It evaluates an expression, and if that expression is True, the indented block beneath it executes. If the expression is False, Python skips the block entirely.
# Basic if statement
temperature = 35
if temperature > 30:
print("It's a hot day.")
print("Stay hydrated!")
Key point: Python uses indentation (typically 4 spaces) to define the body of an if block. The first line that returns to the previous indentation level marks the end of the block. There are no braces, no end if — just consistent indentation.
# Age verification example
user_age = 21
if user_age >= 18:
print("Access granted. You are old enough to enter.")
print("This line runs regardless of the condition.")
if-else StatementWhen you need to handle both outcomes of a condition, use if-else. The else block runs when the if condition evaluates to False.
# Age verification with else
age = 16
if age >= 18:
print("You are eligible to vote.")
else:
print(f"You must wait {18 - age} more year(s) to vote.")
# Even or odd check
number = 7
if number % 2 == 0:
print(f"{number} is even.")
else:
print(f"{number} is odd.")
if-elif-else ChainWhen you have more than two possible outcomes, chain conditions together with elif (short for “else if”). Python evaluates each condition from top to bottom and executes the first block whose condition is True. If none match, the else block runs as a fallback.
# Grade calculator
score = 82
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: 82, Grade: B
Notice the order matters. Since Python stops at the first match, we check from highest to lowest. If we reversed the order, a score of 95 would match score >= 60 first and incorrectly receive a “D”.
# Simple calculator using if-elif
num1 = 15
num2 = 4
operator = "/"
if operator == "+":
result = num1 + num2
elif operator == "-":
result = num1 - num2
elif operator == "*":
result = num1 * num2
elif operator == "/":
if num2 != 0:
result = num1 / num2
else:
result = "Error: Division by zero"
else:
result = "Error: Unknown operator"
print(f"{num1} {operator} {num2} = {result}")
# Output: 15 / 4 = 3.75
# User role-based access control
user_role = "editor"
if user_role == "admin":
print("Full access: read, write, delete, manage users")
elif user_role == "editor":
print("Editor access: read and write")
elif user_role == "viewer":
print("Viewer access: read only")
else:
print("Unknown role. Access denied.")
You can place if statements inside other if statements. This is useful when a secondary condition only makes sense after a primary condition is met.
# Nested conditional: age and membership check
age = 25
has_membership = True
if age >= 18:
if has_membership:
print("Welcome! You have full access.")
else:
print("You are old enough, but you need a membership.")
else:
print("Sorry, you must be 18 or older.")
# Login validation with nested checks
username = "folauk"
password = "secure123"
is_active = True
if username == "folauk":
if password == "secure123":
if is_active:
print("Login successful.")
else:
print("Account is deactivated.")
else:
print("Incorrect password.")
else:
print("User not found.")
A word of caution: While nesting works, deeply nested conditionals (3+ levels) become difficult to read and maintain. If you find yourself going deeper than two levels, consider refactoring with early returns, guard clauses, or by extracting logic into helper functions. Here is the same login example refactored:
# Refactored: use early returns to avoid deep nesting
def authenticate(username, password, is_active):
if username != "folauk":
return "User not found."
if password != "secure123":
return "Incorrect password."
if not is_active:
return "Account is deactivated."
return "Login successful."
result = authenticate("folauk", "secure123", True)
print(result)
and, or, notLogical operators let you combine multiple conditions into a single expression. Mastering them is essential for writing expressive, readable conditionals.
and — Both conditions must be True# Both conditions must be True
age = 25
has_license = True
if age >= 18 and has_license:
print("You are allowed to drive.")
else:
print("You cannot drive.")
# Checking a value is within a range
score = 85
if score >= 80 and score <= 89:
print("You got a B.")
or — At least one condition must be True# At least one condition must be True
is_weekend = True
is_holiday = False
if is_weekend or is_holiday:
print("You can sleep in today!")
else:
print("Time to get up for work.")
# User role check with or
role = "moderator"
if role == "admin" or role == "moderator":
print("You can delete posts.")
not — Inverts a boolean# Invert a condition
is_logged_in = False
if not is_logged_in:
print("Please log in to continue.")
# Complex condition with and, or, not
age = 30
is_student = False
has_coupon = True
if (age < 18 or is_student) and not has_coupon:
print("Standard discount applied.")
elif has_coupon:
print("Coupon discount applied.")
else:
print("No discount available.")
Operator precedence: not is evaluated first, then and, then or. When in doubt, use parentheses to make your intent explicit. Your future self (and your teammates) will thank you.
Python does not require conditions to be strictly True or False. Every object in Python has an inherent boolean value. Understanding what is “truthy” and “falsy” will make your conditionals more Pythonic.
Falsy values (evaluate to False):
FalseNone0 (integer zero), 0.0 (float zero), 0j (complex zero)"" (empty string)[] (empty list), () (empty tuple), {} (empty dict), set() (empty set)Everything else is truthy, including non-zero numbers, non-empty strings, and non-empty collections.
# Truthy/Falsy in action
name = ""
if name:
print(f"Hello, {name}!")
else:
print("Name is empty. Please provide a name.")
# Checking if a list has items
shopping_list = ["milk", "eggs"]
if shopping_list:
print(f"You have {len(shopping_list)} item(s) to buy.")
else:
print("Your shopping list is empty.")
# Using truthiness for None checks
config = None
if not config:
print("No configuration loaded. Using defaults.")
This is idiomatic Python. Instead of writing if len(my_list) > 0:, experienced Python developers write if my_list:. It is cleaner, faster, and communicates intent more directly.
Python supports a one-line if-else expression, often called the ternary operator. The syntax is:
# Syntax: value_if_true if condition else value_if_false age = 20 status = "adult" if age >= 18 else "minor" print(status) # Output: adult
# Ternary for assigning default values
user_input = ""
username = user_input if user_input else "Anonymous"
print(f"Welcome, {username}!") # Output: Welcome, Anonymous!
# Ternary in a function return
def get_access_level(role):
return "full" if role == "admin" else "limited"
print(get_access_level("admin")) # Output: full
print(get_access_level("viewer")) # Output: limited
Use ternary expressions for simple, short conditions. If the logic is complex or involves multiple branches, stick with a regular if-elif-else block for readability.
match-case Statement (Python 3.10+)Python 3.10 introduced structural pattern matching with the match-case statement. If you are familiar with switch statements from other languages, this is Python’s more powerful equivalent. It does not just match values — it can destructure data, match types, and bind variables.
# Basic match-case: HTTP status code handler
status_code = 404
match status_code:
case 200:
print("OK - Request succeeded.")
case 301:
print("Moved Permanently.")
case 404:
print("Not Found - Resource does not exist.")
case 500:
print("Internal Server Error.")
case _:
print(f"Unhandled status code: {status_code}")
The underscore _ is the wildcard pattern — it matches anything and acts as the default case.
# match-case with combined patterns using |
command = "quit"
match command:
case "start" | "run":
print("Starting the application...")
case "stop" | "quit" | "exit":
print("Shutting down...")
case "status":
print("Application is running.")
case _:
print(f"Unknown command: {command}")
# match-case with pattern destructuring
point = (3, 0)
match point:
case (0, 0):
print("Origin")
case (x, 0):
print(f"On the X-axis at x={x}")
case (0, y):
print(f"On the Y-axis at y={y}")
case (x, y):
print(f"Point at ({x}, {y})")
# Output: On the X-axis at x=3
match-case is especially useful for command parsing, event handling, and working with structured data like API responses or ASTs. However, note that it requires Python 3.10 or later, so check your target Python version before using it in production code.
pass StatementIn Python, if statements cannot have an empty body. If you need a placeholder — perhaps you plan to implement the logic later — use the pass statement to avoid a syntax error.
# Placeholder for future implementation
if 3 > 2:
pass # TODO: implement this logic later
In practice, you will use pass during development as a stub. It does nothing but tells Python “I intentionally left this empty.”
These are mistakes that every Python developer makes at some point. Knowing them ahead of time will save you hours of debugging.
= instead of ==# WRONG: = is assignment, not comparison
# if x = 10: # SyntaxError!
# CORRECT: == is comparison
x = 10
if x == 10:
print("x is 10")
Unlike C or Java, Python will raise a SyntaxError if you use = in a condition. This is actually a helpful safeguard, but it still catches beginners off guard.
# WRONG: inconsistent indentation
# if True:
# print("indented with 4 spaces")
# print("indented with 6 spaces") # IndentationError!
# CORRECT: consistent 4-space indentation
if True:
print("line 1")
print("line 2")
Python enforces indentation as part of its syntax. Mix tabs and spaces, or use inconsistent spacing, and you will get an IndentationError. Configure your editor to insert 4 spaces when you press Tab. This is the Python community standard (PEP 8).
# Potential surprise: string vs integer comparison
user_input = "5"
threshold = 5
# This is False because "5" (str) != 5 (int)
if user_input == threshold:
print("Match")
else:
print("No match") # This prints
# Fix: convert types explicitly
if int(user_input) == threshold:
print("Match") # Now this prints
Python does not implicitly convert types during comparison. A string "5" is not equal to an integer 5. Always be explicit about type conversions, especially when working with user input.
# Hard to read
if user.age >= 18 and user.is_active and not user.is_banned and user.email_verified:
grant_access()
# Better: extract to a well-named variable or function
def is_eligible(user):
return (
user.age >= 18
and user.is_active
and not user.is_banned
and user.email_verified
)
if is_eligible(user):
grant_access()
# Deeply nested (avoid this)
def process_order(order):
if order is not None:
if order.is_valid:
if order.in_stock:
ship(order)
# Flat and clear (prefer this)
def process_order(order):
if order is None:
return
if not order.is_valid:
return
if not order.in_stock:
return
ship(order)
# Harder to reason about
if not is_invalid:
process()
# Clearer
if is_valid:
process()
When naming boolean variables and writing conditions, aim for positive phrasing. is_active is easier to reason about than not is_inactive.
# Complete example: Student grade report generator
def calculate_grade(score):
"""Return a letter grade based on the numeric score."""
if not isinstance(score, (int, float)):
return "Invalid"
if score < 0 or score > 100:
return "Invalid"
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
return "F"
def get_status(grade):
"""Return pass/fail status."""
return "Pass" if grade not in ("F", "Invalid") else "Fail"
# Student records
students = [
{"name": "Alice", "score": 92},
{"name": "Bob", "score": 75},
{"name": "Charlie", "score": 58},
{"name": "Diana", "score": 88},
]
for student in students:
grade = calculate_grade(student["score"])
status = get_status(grade)
print(f"{student['name']}: {student['score']}% -> {grade} ({status})")
Output:
Alice: 92% -> A (Pass) Bob: 75% -> C (Pass) Charlie: 58% -> F (Fail) Diana: 88% -> B (Pass)
if, elif, and else block must be consistently indented (use 4 spaces).if-elif-else is the workhorse of Python conditionals. Order your conditions from most specific to least specific.==, !=, <, >, <=, >=) produce boolean values that drive your conditions.and, or, not) let you combine and invert conditions. Remember precedence: not > and > or.0, None, and "" are all falsy. Use this to write concise, Pythonic checks.x if condition else y) are great for simple inline conditionals, but do not overuse them for complex logic.match-case (Python 3.10+) is powerful for pattern matching, especially when working with structured data or command dispatching.