Python Conditionals: if/elif/else, Ternary Expressions, Match-Case (Python 3.10+), Truthy/Falsy Values, Short-Circuit Evaluation, and Decision Patterns

Python Conditionals: if/elif/else, Ternary Expressions, Match-Case (Python 3.10+), Truthy/Falsy Values, Short-Circuit Evaluation, and Decision Patterns

Every program makes decisions. Should this file be processed or skipped? Is this customer active or churned? Did the API return a success or an error? Should the pipeline retry or fail? Every one of these decisions is a conditional — code that runs only when a specific condition is true.

Conditionals are the traffic lights of programming. Green (True) — the code block runs. Red (False) — it is skipped. And just like traffic lights can have multiple signals (green arrow, flashing yellow, red with turn), Python has multiple conditional tools: if/elif/else for standard decisions, ternary expressions for one-liners, and the new match-case (Python 3.10+) for pattern matching.

This post covers conditionals from basic if statements to advanced patterns — including the truthy/falsy system that makes Python uniquely expressive, short-circuit evaluation that most tutorials skip, guard clauses that flatten your code, and match-case for clean multi-branch logic.

Table of Contents

  • The if Statement
  • Basic if
  • if-else
  • if-elif-else
  • Indentation Matters
  • Comparison Operators
  • Equality and Inequality
  • Greater Than, Less Than
  • Chained Comparisons
  • is vs == (Identity vs Equality)
  • Logical Operators
  • and, or, not
  • Short-Circuit Evaluation
  • Using Short-Circuit for Defaults
  • Truthy and Falsy Values
  • What Python Considers False
  • Why This Matters
  • Common Truthy/Falsy Patterns
  • Ternary Expression (Conditional Expression)
  • Basic Syntax
  • Nested Ternary (and Why to Avoid It)
  • Ternary in f-Strings
  • Ternary in List Comprehensions
  • Nested Conditionals vs Guard Clauses
  • The Pyramid of Doom
  • Guard Clauses (Early Return)
  • Match-Case (Python 3.10+)
  • Basic match-case
  • Matching with Patterns
  • Matching with Guards (if conditions)
  • Matching Sequences and Dicts
  • When to Use match-case vs if-elif
  • Common Conditional Patterns
  • Checking for None
  • Checking for Empty Collections
  • Safe Division
  • Boundary Checking
  • Data Validation Pattern
  • Conditionals in Data Engineering
  • Pipeline Branching
  • Error Handling Decisions
  • Data Quality Flags
  • Common Mistakes
  • Interview Questions
  • Wrapping Up

The if Statement

Basic if

# The simplest conditional — run code only if condition is True
age = 25

if age >= 18:
    print("You are an adult")      # Runs because 25 >= 18 is True

# If the condition is False, the block is skipped entirely
temperature = 15

if temperature > 30:
    print("It is hot outside")     # Skipped — 15 > 30 is False

# Nothing happens. No error. The code just moves on.

Real-life analogy: if is like a security guard at a door. “If you have a badge, you may enter.” If you have a badge → you walk in. If not → you keep walking past the door. The guard does not chase you or throw an error — you simply do not enter.

if-else

# Two branches — one ALWAYS runs
age = 15

if age >= 18:
    print("You are an adult")
else:
    print("You are a minor")       # This runs because 15 < 18

# Real-world: check API response
status_code = 404

if status_code == 200:
    print("Success — process data")
else:
    print(f"Error — status code: {status_code}")  # Runs

if-elif-else

# Multiple branches — first True condition wins, rest are skipped
score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"       # This matches (85 >= 80) — stops here
elif score >= 70:
    grade = "C"       # Skipped — already matched above
elif score >= 60:
    grade = "D"       # Skipped
else:
    grade = "F"       # Skipped

print(f"Score: {score}, Grade: {grade}")  # Score: 85, Grade: B

# KEY: Only ONE branch executes. Even if multiple conditions are True,
# Python picks the FIRST one and skips the rest.
# score = 95 → 95 >= 90 is True AND 95 >= 80 is True,
# but only the first match (A) runs.

# Real-world: categorize file sizes
file_size_mb = 250

if file_size_mb > 1000:
    category = "large"
elif file_size_mb > 100:
    category = "medium"     # Matches
elif file_size_mb > 10:
    category = "small"
else:
    category = "tiny"

print(f"{file_size_mb} MB → {category}")  # 250 MB → medium

Real-life analogy: if-elif-else is like a drive-through menu with escalating sizes. “Is it XL? No. Is it Large? No. Is it Medium? Yes — give them Medium. Do not check Small.” The cashier stops at the first match and does not continue asking.

Indentation Matters

# Python uses indentation (4 spaces) to define code blocks
# No curly braces {} like Java/C — indentation IS the structure

if True:
    print("Inside the if block")       # 4 spaces indent
    print("Still inside")             # Same indent = same block
print("Outside the if block")          # No indent = runs always

# WRONG — inconsistent indentation causes IndentationError
# if True:
#     print("line 1")
#       print("line 2")    # IndentationError: unexpected indent

# WRONG — missing indentation
# if True:
# print("oops")            # IndentationError: expected an indented block

# Convention: use 4 SPACES (not tabs). Configure your editor to convert tabs to spaces.

Comparison Operators

Equality and Inequality

# == checks VALUE equality
print(5 == 5)        # True
print(5 == 5.0)      # True (int and float can be equal)
print("hello" == "hello")  # True
print("Hello" == "hello")  # False (case-sensitive!)
print([1, 2] == [1, 2])    # True (same contents)

# != checks inequality
print(5 != 3)        # True
print("a" != "b")    # True
print(5 != 5)        # False

# COMMON BUG: using = instead of ==
# if x = 5:         # SyntaxError! = is assignment, == is comparison
# if x == 5:        # Correct

Greater Than, Less Than

print(10 > 5)     # True
print(10 < 5)     # False
print(10 >= 10)   # True (greater than OR equal)
print(10 <= 9)    # False

# Works with strings (alphabetical/lexicographic order)
print("apple" < "banana")   # True (a comes before b)
print("apple" < "Apple")    # False (lowercase > uppercase in ASCII)

# Works with dates
from datetime import date
print(date(2026, 6, 15) > date(2026, 1, 1))  # True

Chained Comparisons

# Python allows chaining — unique and powerful!
age = 25
if 18 <= age <= 65:
    print("Working age")      # True: 18 <= 25 AND 25 <= 65

# Equivalent to (but more readable than):
if age >= 18 and age <= 65:
    print("Working age")

# Triple chain
x = 5
if 1 < x < 10:
    print("x is between 1 and 10")  # True

# Even this works (checks all adjacent pairs)
if 1 < 3 < 5 < 10:
    print("All true")  # 1<3 AND 3<5 AND 5<10 → True

# Data engineering: check if value is in expected range
row_count = 5000
if 0 < row_count < 1_000_000:
    print("Row count is reasonable")
else:
    print("WARNING: Row count is suspicious")

is vs == (Identity vs Equality)

# == checks VALUE: do these two have the same content?
# is checks IDENTITY: are these the exact same OBJECT in memory?

a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)    # True — same value (same contents)
print(a is b)    # False — different objects (different memory addresses)

c = a
print(a is c)    # True — same object (c is another label for a)

# RULES:
# Use == for comparing VALUES (almost always what you want)
# Use "is" ONLY for None, True, False

# ✅ Correct:
if result is None:
    print("No result")

# ❌ Wrong (works by coincidence due to interning, but unreliable):
# if x is 42:      # Might work for small ints, fails for large ones
# if name is "hello":  # Might work due to string interning, but unreliable

# ✅ Correct:
if x == 42:
    print("x is 42")

Logical Operators

and, or, not

# and — both must be True
age = 25
has_license = True
if age >= 18 and has_license:
    print("Can drive")   # Both True → runs

# or — at least one must be True
is_weekend = False
is_holiday = True
if is_weekend or is_holiday:
    print("Day off!")    # One is True → runs

# not — flips True/False
is_blocked = False
if not is_blocked:
    print("Access granted")  # not False = True → runs

# Combining all three
age = 25
is_student = True
is_senior = False
if (age >= 18 and is_student) or is_senior:
    print("Eligible for discount")

# Precedence: not > and > or
# Use parentheses for clarity — never rely on memorizing precedence

Short-Circuit Evaluation

Python stops evaluating as soon as the result is determined. This is not just a performance optimization — it is a feature you can use intentionally.

# AND short-circuits: if the first is False, the second is NEVER evaluated
x = 0
if x != 0 and 10 / x > 2:      # x is 0, so "x != 0" is False
    print("Result")             # 10/x is NEVER evaluated — no ZeroDivisionError!
# Without short-circuit, 10/0 would crash

# OR short-circuits: if the first is True, the second is NEVER evaluated
logged_in = True
if logged_in or check_credentials():   # logged_in is True
    print("Welcome")                    # check_credentials() is NEVER called

# This is why ORDER MATTERS in conditions:
# Put the CHEAP/FAST check first, expensive check second
# ✅ if is_cached and fetch_from_database():   ← checks cache first
# ❌ if fetch_from_database() and is_cached:   ← always hits database

Using Short-Circuit for Defaults

# Python's and/or return the ACTUAL VALUE, not just True/False
# This enables powerful one-liner defaults

# or returns the first truthy value (or the last value if all falsy)
name = "" or "Unknown"
print(name)           # "Unknown" — "" is falsy, so or returns "Unknown"

name = "Naveen" or "Unknown"
print(name)           # "Naveen" — "Naveen" is truthy, short-circuits

# Practical: default values
username = input_name or "Guest"
db_host = config.get("host") or "localhost"
page_size = user_pref or 25

# and returns the first falsy value (or the last value if all truthy)
result = "hello" and "world"
print(result)         # "world" — both truthy, returns last

result = "" and "world"
print(result)         # "" — first is falsy, short-circuits, returns ""

# Practical: safe attribute access
# name = user and user.name   ← if user is None, returns None (not AttributeError)

Truthy and Falsy Values

What Python Considers False

In Python, EVERY value has a boolean interpretation. You do not need to write if x == True or if len(my_list) > 0. Python automatically evaluates values as True or False in boolean contexts.

Falsy (evaluates to False) Truthy (evaluates to True)
FalseTrue
0, 0.0, 0jAny non-zero number
"" (empty string)Any non-empty string (including "False"!)
[] (empty list)Any non-empty list
() (empty tuple)Any non-empty tuple
{} (empty dict)Any non-empty dict
set() (empty set)Any non-empty set
NoneEverything else

# Proof
print(bool(0))          # False
print(bool(42))         # True
print(bool(""))         # False
print(bool("hello"))    # True
print(bool([]))         # False
print(bool([1, 2]))     # True
print(bool(None))       # False
print(bool("0"))        # True! — non-empty string (even "0", "False", "None")
print(bool("False"))    # True! — this trips everyone up

Why This Matters

# VERBOSE (unnecessary):
if len(my_list) > 0:
    print("List has items")

if name != "" and name is not None:
    print("Name exists")

if count == True:
    print("Count is true")

# PYTHONIC (clean):
if my_list:
    print("List has items")

if name:
    print("Name exists")

if count:
    print("Count is truthy")

Real-life analogy: Asking “Is the box empty?” is more natural than asking “Is the number of items in the box greater than zero?” Python’s truthy/falsy system lets you write code the natural way — if box instead of if len(box) > 0.

Common Truthy/Falsy Patterns

# Pattern 1: Check for data before processing
data = fetch_from_api()
if data:                    # Truthy = has content (not None, not [], not "")
    process(data)
else:
    print("No data received")

# Pattern 2: Provide defaults using or
name = user_input or "Guest"           # "" → "Guest", "Naveen" → "Naveen"
page_size = params.get("limit") or 25  # None → 25, 0 → 25 (careful!)

# Pattern 3: Validate before using
results = query_database()
if not results:             # Empty list or None
    print("No results found")
    return

# Pattern 4: Filter truthy values from a list
values = [0, 1, "", "hello", None, 42, [], [1, 2]]
truthy_only = [v for v in values if v]
print(truthy_only)  # [1, 'hello', 42, [1, 2]]

# GOTCHA with "or" for defaults:
count = 0
result = count or 10    # 10! Because 0 is falsy
# If 0 is a valid value, use: result = count if count is not None else 10

Ternary Expression (Conditional Expression)

Basic Syntax

# Syntax: value_if_true IF condition ELSE value_if_false

# Standard if-else (4 lines):
if age >= 18:
    status = "adult"
else:
    status = "minor"

# Ternary (1 line — same result):
status = "adult" if age >= 18 else "minor"

# More examples:
label = "even" if x % 2 == 0 else "odd"
greeting = f"Good morning" if hour < 12 else "Good afternoon"
discount = 0.2 if is_member else 0.0
color = "red" if errors > 0 else "green"

# Data engineering:
load_type = "incremental" if has_watermark else "full"
file_format = "parquet" if size_mb > 100 else "csv"

Nested Ternary (and Why to Avoid It)

# You CAN nest ternaries — but SHOULD you?
grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "F"

# This works but is HARD TO READ. Compare with if-elif:
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

# RULE: Use ternary for simple two-way decisions.
# Use if-elif-else for three or more branches.
# Code is read 10x more than it is written — readability wins.

Ternary in f-Strings

# Embed conditionals directly in formatted strings
count = 1
print(f"Found {count} {'item' if count == 1 else 'items'}")
# "Found 1 item" — handles pluralization

count = 5
print(f"Found {count} {'item' if count == 1 else 'items'}")
# "Found 5 items"

# Status messages
status = "completed"
print(f"Pipeline {'succeeded' if status == 'completed' else 'failed'}!")
# "Pipeline succeeded!"

Ternary in List Comprehensions

# Transform values conditionally
nums = [1, -2, 3, -4, 5, -6]
absolute = [x if x >= 0 else -x for x in nums]
print(absolute)  # [1, 2, 3, 4, 5, 6]

# Label values
labels = ["positive" if x > 0 else "negative" if x < 0 else "zero" for x in nums]
print(labels)    # ['positive', 'negative', 'positive', 'negative', ...]

# Note the difference:
# [expr IF cond ELSE expr FOR x IN list]  ← ternary in EXPRESSION (transforms every item)
# [expr FOR x IN list IF cond]            ← filter (keeps only matching items)

Nested Conditionals vs Guard Clauses

The Pyramid of Doom

# BAD — deeply nested "pyramid of doom"
def process_order(order):
    if order is not None:
        if order.get("status") == "active":
            if order.get("items"):
                if order.get("payment"):
                    if order["payment"].get("verified"):
                        # Finally! The actual logic buried 5 levels deep
                        total = sum(item["price"] for item in order["items"])
                        return total
                    else:
                        return "Payment not verified"
                else:
                    return "No payment info"
            else:
                return "No items"
        else:
            return "Order not active"
    else:
        return "No order"

# This is hard to read, hard to maintain, and a sign of poor structure

Guard Clauses (Early Return)

# GOOD — flat, readable guard clauses (early return)
def process_order(order):
    if order is None:
        return "No order"

    if order.get("status") != "active":
        return "Order not active"

    if not order.get("items"):
        return "No items"

    if not order.get("payment"):
        return "No payment info"

    if not order["payment"].get("verified"):
        return "Payment not verified"

    # Happy path — clean and unindented
    total = sum(item["price"] for item in order["items"])
    return total

# Each guard clause handles ONE error and exits immediately
# The actual logic runs only if ALL guards pass
# No nesting, no pyramid, easy to add new checks

Real-life analogy: Guard clauses are like airport security. Each checkpoint handles one thing: “No ticket? Go back. No ID? Go back. Bag too heavy? Go back. Metal detected? Go back.” Only if you pass ALL checkpoints do you reach the gate. Each checkpoint is independent — not nested inside the previous one.

Match-Case (Python 3.10+)

match-case is Python’s answer to switch-case in other languages. But it is more powerful — it can match patterns, destructure sequences, and match types.

Basic match-case

# Replace if-elif chains with cleaner match-case
status_code = 404

match status_code:
    case 200:
        message = "OK — Success"
    case 201:
        message = "Created"
    case 400:
        message = "Bad Request"
    case 401:
        message = "Unauthorized"
    case 403:
        message = "Forbidden"
    case 404:
        message = "Not Found"           # This matches
    case 500:
        message = "Internal Server Error"
    case _:                              # Wildcard — matches anything (like else)
        message = f"Unknown status: {status_code}"

print(message)  # "Not Found"

# The underscore _ is the wildcard pattern — catches everything not matched above
# It MUST be the last case (like else in if-elif-else)

Matching with Patterns

# Match multiple values with | (OR pattern)
match day:
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
        day_type = "Weekday"
    case "Saturday" | "Sunday":
        day_type = "Weekend"
    case _:
        day_type = "Invalid day"

# Match and CAPTURE values
command = ("move", 10, 20)

match command:
    case ("quit",):
        print("Quitting")
    case ("move", x, y):           # Captures x=10, y=20
        print(f"Moving to ({x}, {y})")
    case ("shoot", direction):
        print(f"Shooting {direction}")
    case _:
        print("Unknown command")

Matching with Guards (if conditions)

# Add conditions to case blocks using "if" (guards)
age = 25

match age:
    case n if n < 0:
        category = "Invalid"
    case n if n < 13:
        category = "Child"
    case n if n < 18:
        category = "Teenager"
    case n if n < 65:
        category = "Adult"         # Matches: 25 < 65
    case _:
        category = "Senior"

print(f"Age {age}: {category}")  # Age 25: Adult

# Data engineering: categorize file processing results
match (rows_read, rows_written, error_count):
    case (0, 0, 0):
        status = "Empty source — nothing to process"
    case (n, m, 0) if n == m:
        status = f"Success — {n} rows loaded"
    case (n, m, 0) if n > m:
        status = f"Warning — {n - m} rows filtered out"
    case (_, _, e) if e > 0:
        status = f"Errors — {e} rows failed"
    case _:
        status = "Unknown result"

Matching Sequences and Dicts

# Match list patterns
match data:
    case []:
        print("Empty list")
    case [single]:
        print(f"One item: {single}")
    case [first, second]:
        print(f"Two items: {first}, {second}")
    case [first, *rest]:
        print(f"First: {first}, remaining: {rest}")

# Match dict patterns (Python 3.10+)
match event:
    case {"type": "login", "user": username}:
        print(f"{username} logged in")
    case {"type": "purchase", "amount": amount} if amount > 100:
        print(f"Large purchase: ${amount}")
    case {"type": "error", "message": msg}:
        print(f"Error: {msg}")
    case _:
        print("Unknown event")

When to Use match-case vs if-elif

Scenario Use if-elif Use match-case
2-3 conditionsYes (simpler)Overkill
Comparing against many fixed valuesVerboseYes (cleaner)
Range checks (x > 10, x < 50)Yes (natural)Possible with guards but less natural
Destructuring sequences/dictsMessyYes (designed for this)
Status codes, command parsingOkayYes (ideal use case)
Need Python 3.9 compatibilityYes (only option)Not available

Common Conditional Patterns

Checking for None

# Always use "is None" / "is not None" — never == None
result = fetch_data()

# ✅ Correct:
if result is None:
    print("No data")

if result is not None:
    process(result)

# ❌ Wrong:
# if result == None:     # Works but violates PEP 8 and can be overridden
# if not result:         # Catches None BUT ALSO [], "", 0 — too broad!

Checking for Empty Collections

# ✅ Pythonic — use truthy/falsy
if my_list:
    print("Has items")

if not my_dict:
    print("Empty dict")

# ❌ Unpythonic
# if len(my_list) > 0:    # Works but verbose
# if my_list != []:        # Works but ugly
# if len(my_list) != 0:   # Just... no

Safe Division

# Guard against ZeroDivisionError
total = 100
count = 0

# Pattern 1: if check
average = total / count if count != 0 else 0

# Pattern 2: short-circuit
average = count and total / count  # 0 (count is falsy, returns 0, skips division)

# Pattern 3: try/except (for unexpected zeros)
try:
    average = total / count
except ZeroDivisionError:
    average = 0

Boundary Checking

# Check if a value is within expected boundaries
def validate_age(age):
    if not isinstance(age, (int, float)):
        return False, "Age must be a number"
    if age < 0:
        return False, "Age cannot be negative"
    if age > 150:
        return False, "Age seems unrealistic"
    return True, "Valid"

valid, message = validate_age(25)
print(f"{valid}: {message}")  # True: Valid

valid, message = validate_age(-5)
print(f"{valid}: {message}")  # False: Age cannot be negative

Data Validation Pattern

# Validate a data record using guard clauses
def validate_customer(record):
    errors = []

    if not record.get("customer_id"):
        errors.append("Missing customer_id")

    if not record.get("email"):
        errors.append("Missing email")
    elif "@" not in record["email"]:
        errors.append("Invalid email format")

    if record.get("age") is not None:
        if not isinstance(record["age"], (int, float)):
            errors.append("Age must be numeric")
        elif record["age"] < 0 or record["age"] > 150:
            errors.append("Age out of range")

    return {"valid": len(errors) == 0, "errors": errors}

# Usage
result = validate_customer({"customer_id": 1001, "email": "test", "age": -5})
print(result)
# {'valid': False, 'errors': ['Invalid email format', 'Age out of range']}

Conditionals in Data Engineering

Pipeline Branching

# Decide processing strategy based on data characteristics
row_count = get_source_row_count()
last_load = get_last_load_timestamp()

if row_count == 0:
    print("Source is empty — skipping pipeline")
elif last_load is None:
    print("First run — performing full load")
    run_full_load()
elif row_count > 10_000_000:
    print("Large dataset — using parallel load")
    run_parallel_incremental_load()
else:
    print("Standard incremental load")
    run_incremental_load(since=last_load)

Error Handling Decisions

# Decide what to do based on error type
def handle_pipeline_error(error_type, table_name, retry_count):
    if error_type == "timeout" and retry_count < 3:
        print(f"Retrying {table_name} (attempt {retry_count + 1})")
        return "retry"
    elif error_type == "auth_expired":
        print("Refreshing authentication token")
        return "refresh_and_retry"
    elif error_type == "source_unavailable":
        print(f"Source down — skipping {table_name}, will retry next run")
        return "skip"
    else:
        print(f"Fatal error on {table_name}: {error_type}")
        return "fail"

Data Quality Flags

# Flag data quality issues using conditionals
def add_quality_flags(record):
    flags = []

    if record.get("email") and "@" not in record["email"]:
        flags.append("invalid_email")

    if record.get("age") is not None and (record["age"] < 0 or record["age"] > 120):
        flags.append("suspicious_age")

    if record.get("city") and record["city"].isdigit():
        flags.append("numeric_city")

    if not record.get("customer_id"):
        flags.append("missing_id")

    record["quality_flags"] = flags
    record["is_clean"] = len(flags) == 0
    return record

Common Mistakes

  1. Using = instead of ==if x = 5 is assignment (SyntaxError). if x == 5 is comparison. Python catches this as a syntax error, but it is a common typo.
  2. Using == to check for None — use is None and is not None. == can be overridden by custom classes to return True for non-None values. is checks identity and cannot be overridden.
  3. Using if not x when you mean if x is Nonenot x is True for None, 0, “”, [], {} — all falsy values. If 0 or “” are valid values in your context, not x is too broad. Use x is None for explicit None checks.
  4. Forgetting that bool("False") is True — the string "False" is non-empty, so it is truthy. If you receive “true”/”false” strings from a config or API, use value.lower() == "true", not bool(value).
  5. Deep nesting instead of guard clauses — if your code has more than 3 levels of nesting, refactor using guard clauses (early return). Flat code is easier to read, test, and maintain.
  6. Using or for defaults when 0 is validcount = user_count or 10 gives 10 when user_count is 0 (because 0 is falsy). If 0 is a valid value, use count = user_count if user_count is not None else 10.
  7. Not using chained comparisons — writing if x >= 0 and x <= 100 instead of if 0 <= x <= 100. Chaining is more readable and more Pythonic.

Interview Questions

Q: What are truthy and falsy values in Python? A: Every Python value has a boolean interpretation. Falsy values (evaluate to False): False, 0, 0.0, "", [], (), {}, set(), None. Everything else is truthy. This is why if my_list: works — an empty list is falsy, a non-empty list is truthy.

Q: What is short-circuit evaluation? A: Python stops evaluating boolean expressions as soon as the result is determined. For and, if the first operand is False, the second is never evaluated. For or, if the first is True, the second is never evaluated. This prevents errors (e.g., x != 0 and 10/x avoids division by zero) and enables defaults (e.g., name or "Guest").

Q: What is the ternary expression in Python? A: value_if_true if condition else value_if_false — a one-line conditional assignment. Example: status = "adult" if age >= 18 else "minor". Use for simple two-way decisions. Avoid nesting ternaries — use if-elif-else for 3+ branches.

Q: Why use is None instead of == None? A: is checks identity (same object in memory) and cannot be overridden. == checks equality and CAN be overridden by custom classes via __eq__. Since None is a singleton (only one None object exists), is None is both correct and faster.

Q: What are guard clauses and why are they better than nested ifs? A: Guard clauses check for invalid conditions at the start of a function and return immediately. Instead of nesting 5 levels deep, each guard handles one error case and exits. The “happy path” (actual logic) runs only if all guards pass, and it stays at the top indentation level — flat, readable, and easy to maintain.

Q: What is match-case in Python and when would you use it? A: Introduced in Python 3.10, match-case is a structural pattern matching statement. It matches values against patterns — constants, sequences, dicts, types, or even captured variables. Use it for HTTP status code handling, command parsing, event processing, and any scenario with many discrete cases. It is cleaner than long if-elif chains for matching against fixed values.

Wrapping Up

Conditionals are the decision-making backbone of every program. Master if/elif/else for standard branching, ternary expressions for clean one-liners, and match-case for multi-value pattern matching. Understand truthy/falsy values to write Pythonic code (if my_list instead of if len(my_list) > 0). Use short-circuit evaluation for safe access and defaults. And flatten your code with guard clauses instead of building pyramids.

The mark of an experienced Python developer is not just knowing the syntax — it is knowing when to use which conditional pattern and keeping the code flat, readable, and intentional.

Previous in this series: Dictionaries — Nesting, Comprehensions, and DefaultDict

Next in this series: Loops — for, while, enumerate, zip, break/continue


Naveen Vuppula is a Senior Data Engineering Consultant and app developer based in Ontario, Canada. He writes about Python, SQL, AWS, Azure, and everything data engineering at DriveDataScience.com.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top
Share via
Copy link