A conditional runs one block of code or another, depending on whether something is true.
age = 20if age >= 18:
print("Adult")
else:
print("Minor")# AdultPython’s version has three things worth knowing early. There are no braces — indentation defines the block. The middle branch is elif, short for else if. And if x = 5 is a SyntaxError, not a silent bug, which is a deliberate improvement on C.
This guide covers the syntax, truthiness, the ternary operator, and when a chain of elif should be something else.
| Blocks defined by | Indentation — no braces anywhere |
elif is short for | else if — and there’s no limit to how many |
| Only the first match runs | Conditions are checked in order, then it stops |
| One-line form | x if condition else y |
if x = 5 | A SyntaxError, on purpose |
Table of Contents
The syntax
if condition:
# runs when the condition is true
else:
# runs when it isn't⚠️ Two things are mandatory: the colon at the end of the line, and the indentation of the block. Python has no braces — the indentation is the block.
Four spaces is the convention, and PEP 8 recommends it. What matters is consistency: mixing tabs and spaces raises TabError.
The else is optional:
if temperature > 30:
print("It's hot")
# nothing happens otherwiseelif: more than two branches
elif is short for else if, and you can chain as many as you need.
score = 85if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"print(grade) # B⚠️ Conditions are checked in order, and it stops at the first match. Once score >= 80 is true, the rest aren’t even evaluated.
That ordering matters more than it looks:
# Wrong order — everything above 70 gets a C
if score >= 70:
grade = "C"
elif score >= 80:
grade = "B" # unreachable for any scoreA score of 95 matches the first condition and stops there. ⚠️ With overlapping ranges, put the most restrictive condition first.
elif vs several separate ifs
# elif — only one runs
if x > 10:
print("big")
elif x > 5:
print("medium")# separate ifs — both can run
if x > 10:
print("big")
if x > 5:
print("medium")With x = 20, the first prints “big” and the second prints both. They’re different constructs, not styles.
⚠️ Truthiness: what counts as true

A condition doesn’t have to be a comparison. Python evaluates any value as true or false:
if items: # true if the list has anything in it
process(items)if name: # true if the string isn't empty
greet(name)These values are false:
False 0 0.0 "" [] {} set() NoneEverything else is true — including "0", "False" and [0], because a non-empty string and a non-empty list are true regardless of what’s inside.
The bug this causes
def check(quantity):
if not quantity:
return "not informed"
return quantitycheck(None) # not informed ← correct
check(0) # not informed ← WRONG, zero is a real value⚠️ if not x means “empty or absent”. When you mean specifically absent, test for None:
if quantity is None:
return "not informed"Our guide on None in Python covers the distinction in full.
Comparison operators
== equal != not equal
< less than > greater than
<= less or equal >= greater or equal
in contains is same object= in a condition is a SyntaxError
if x = 5:
# SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?⚠️ Python refuses to compile it, and even suggests the fix. In C this compiles and silently assigns, which is one of the classic sources of bugs in that language — Python removed the possibility entirely.
is compares identity, not value
if value is None: # correct — None is a singleton
if name is "Ana": # wrong — compares object identity
if name == "Ana": # correct for valuesUse is only with None, True and False. For everything else, ==.
Chained comparisons
if 1 < x < 10: # valid Python
if x > 1 and x < 10: # same thing, longerPython allows the mathematical form, and it’s clearer. Most languages don’t.
Comparing floats
0.1 + 0.2 == 0.3 # Falseimport math
math.isclose(0.1 + 0.2, 0.3) # True⚠️ Never compare floats with ==. Binary representation makes the result slightly off, and math.isclose() exists for exactly this.
and, or and not
if age >= 18 and has_id:
print("Allowed")if day == "Saturday" or day == "Sunday":
print("Weekend")if not logged_in:
redirect()Two behaviours worth knowing.
They short-circuit. In a and b, if a is false, b is never evaluated — which is what makes this safe:
if user is not None and user.is_active:
...
# the second check never runs when user is NoneAnd they return a value, not a boolean:
name = user_input or "Anonymous" # uses the default when empty⚠️ Which carries the falsy trap again: quantity or 10 replaces a legitimate zero with 10.
The ternary operator
A conditional on one line, when both branches are just a value.
status = "adult" if age >= 18 else "minor"# the long form
if age >= 18:
status = "adult"
else:
status = "minor"The order reads oddly at first — value, condition, alternative — but it’s the standard form, and Python has no ? :.
⚠️ Use it only when both branches are a single expression. Nesting ternaries produces lines nobody can read:
g = "A" if s>=90 else "B" if s>=80 else "C" if s>=70 else "F" # don'tNested conditionals, and how to avoid them
if user is not None:
if user.is_active:
if user.has_permission:
do_something()
else:
print("No permission")
else:
print("Inactive")
else:
print("Not found")Three levels, and the interesting code is buried at the deepest one. ⚠️ Note there are no braces — the indentation alone defines each level, which is why consistency matters so much in Python.
The guard clause inverts it:
if user is None:
return "Not found"
if not user.is_active:
return "Inactive"
if not user.has_permission:
return "No permission"do_something() # the happy path, unindentedHandle the failures first and leave early. The main logic ends up at the top level, and each condition is read independently.
When a chain of elif should be something else
Two alternatives for long chains — and neither is a replacement for a simple if.
match/case, for matching a value
match status_code:
case 200:
return "OK"
case 404:
return "Not Found"
case 500 | 502 | 503:
return "Server Error"
case _:
return "Unknown"⚠️ Available since Python 3.10 — on older versions it’s a SyntaxError, not a missing feature. Our guide on match/case covers it.
A dictionary, for many options
operations = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
}handler = operations.get(op)
if handler is None:
raise ValueError(f"Unknown operation: {op}")result = handler(2, 3) # 5Better when the mapping is data rather than logic, or when it changes at runtime. A fifty-entry dictionary is fine; a fifty-branch elif isn’t.
⚠️ Keep if/elif for ranges and boolean conditions — neither alternative handles score >= 80 well.
if in comprehensions
# Filtering — the if goes at the end
evens = [x for x in range(10) if x % 2 == 0]# Choosing a value — the ternary goes at the front
labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]⚠️ The position tells you which one it is. At the end it filters; at the front it’s a ternary choosing between two values. The second form can’t have an if without an else, because every item needs a value.
The walrus operator
if (n := len(items)) > 10:
print(f"Too many: {n}"):= assigns and tests in one expression, available since Python 3.8. It avoids computing something twice when you need both the test and the value.
⚠️ Conditionals on a server
One pattern matters more than the others: failing safely.
# Risky — an unexpected value falls through silently
if role == "admin":
grant_full_access()
elif role == "user":
grant_read_access()# Safe — anything unexpected is denied
if role == "admin":
grant_full_access()
elif role == "user":
grant_read_access()
else:
deny()⚠️ A chain without an else does nothing when nothing matches — and in an access check, “does nothing” can mean the previous state persists. Always close the chain with an else, even if it only logs.
And a second one: don’t confuse absent with empty in configuration.
timeout = os.environ.get("TIMEOUT")if not timeout: # an explicit "0" is also falsy
timeout = 30if timeout is None: # only when the variable isn't set
timeout = 30Someone who deliberately set TIMEOUT=0 gets 30 with the first version — the kind of bug that only appears in one environment.
An access check with no final else leaves the previous state in place, and nothing in the log says so. Copahost VPS plans give you root access, logs you can actually read, and your choice of Python version. From €3.99/month, with snapshots.
Frequently asked questions
What does elif mean in Python?
It’s short for else if — a condition checked only when the ones above it were false. You can chain as many as you need, and only the first match runs.
Why does Python use indentation instead of braces?
Because indentation is the block. There are no braces anywhere in the language — the colon opens the block and the indentation defines its extent. ⚠️ Mixing tabs and spaces raises TabError, so pick one; four spaces is the convention.
What happens if I write if x = 5?
A SyntaxError — Python refuses to compile it and suggests == or :=. ⚠️ In C the same line compiles and silently assigns, which is a classic bug source. Python removed the possibility on purpose.
What counts as false in Python?
Eight values: False, 0, 0.0, "", [], {}, set() and None. Everything else is true — including "0" and [0], because a non-empty string or list is true regardless of contents.
What’s the difference between if not x and if x is None?
if not x is true for any falsy value — zero, an empty string, an empty list. if x is None is true only for None. ⚠️ Use the second when absence and emptiness mean different things, which in form data and configuration they usually do.
Should I use is or == to compare?
is compares object identity; == compares value. Use is only with None, True and False — for everything else, ==. Python even warns when you use is with a literal.
Can I check two conditions in one line?
Yes, with and and or — and Python also allows the mathematical form: if 1 < x < 10 is valid and clearer than x > 1 and x < 10. Most languages don’t allow it.
What’s the ternary operator in Python?
value_if_true if condition else value_if_false — a conditional in one line. Python has no ? :. ⚠️ Use it only when both branches are a single expression; nested ternaries are unreadable.
Why is 0.1 + 0.2 == 0.3 false?
Because binary can’t represent those decimals exactly, so the sum is 0.30000000000000004. ⚠️ Never compare floats with == — use math.isclose(), which exists for this.
How do I avoid deeply nested ifs?
With guard clauses: handle the failure cases first and return early, leaving the main logic at the top level. Three nested levels usually become three flat checks followed by the real work.
When should I use match/case instead of elif?
When you’re matching a value against several options, or matching the shape of data. ⚠️ It needs Python 3.10 — on older versions it’s a SyntaxError, not a missing feature. Keep elif for ranges and boolean conditions.
Why does my elif branch never run?
Usually because an earlier condition already covers it. ⚠️ With overlapping ranges, order matters — if score >= 70 before elif score >= 80 means no score ever reaches the second, because 95 matches the first and the chain stops.
Conclusion
Conditionals in Python are simple to write and easy to get subtly wrong — and most of the trouble comes from what counts as true.
Three things carry it. if not x is true for zero, empty strings and empty lists, not just None — so when absence and emptiness differ, test is None. Order matters in an elif chain, because only the first match runs and a broad condition placed first makes the rest unreachable. And if x = 5 is a SyntaxError, which is Python removing one of C’s classic bugs rather than a limitation.
And the one that matters on a server: a chain of elif with no final else does nothing when nothing matches. In an access check, “does nothing” means the previous state stays — and the log has nothing to say about it. Close the chain, even if the else only writes a line.
