If and Else
Conditionals decide which code runs from whether something is true or false. Python uses if, elif, and else; one condition is checked and one branch runs. Truthiness and short-circuit behavior cause most of the gotchas, so they’re worth getting straight.
The if Statement
The simplest form runs a block only when a condition is true:
if condition:
# block runs only when condition is true
statement_1
statement_2The condition can be any expression. Python evaluates it in a Boolean context and runs the block when the result is truthy. A colon after the condition is required; the block is everything indented under the if. The first line that isn’t indented more ends the block.
x = 6
if x > 4:
print("x is greater than 4")
# Output: x is greater than 4If the condition is false, the block is skipped and execution continues after it.
if and else
else runs a different block when the condition is false. Exactly one of the two blocks runs.
if condition:
# when condition is true
block_a
else:
# when condition is false
block_bscore = 8
if score >= 6:
print("Pass")
else:
print("Fail")
# Output: PassThe else has to align with the if it belongs to. There’s no condition on else; it just means “when the if condition is false.”
if, elif, and else
More than two outcomes? Use elif (else-if). Python evaluates the conditions in order; the first one that’s true runs its block and the rest are skipped. else, if present, runs only when every if and elif condition is false.
if condition_1:
block_1
elif condition_2:
block_2
elif condition_3:
block_3
else:
block_elseAt most one block runs. When it’s done, execution continues after the whole chain.
status_code = 4
if status_code == 2:
label = "pending"
elif status_code == 4:
label = "active"
elif status_code == 6:
label = "done"
else:
label = "archived"
# label is "active"Order matters. If condition_1 is true, condition_2 and condition_3 are never evaluated, so put the most specific or most likely cases first when that affects correctness or performance.
Condition Expressions
Conditions are built from comparisons and logical operators.
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
== | Equal to | x == 6 |
!= | Not equal to | x != 4 |
< | Less than | x < 8 |
<= | Less or equal | x <= 8 |
> | Greater than | x > 2 |
>= | Greater or equal | x >= 2 |
is | Same object | x is None |
is not | Not same object | x is not None |
a, b = 4, 8
a == 4 # True
a != b # True
a < b # True
a >= 4 # TrueIdentity vs equality: is and is not compare identity: whether two names refer to the same object in memory. Use them for None (and sometimes singletons like True/False). For values (numbers, strings, collections), use == and !=. if x == None works, but if x is None is the usual style; for “not None” use if x is not None.
Logical Operators
| Operator | Meaning | Example |
|---|---|---|
and | Both true | a > 2 and a < 10 |
or | At least one true | a < 2 or a > 8 |
not | Negate | not (a == 4) |
x = 6
x > 2 and x < 10 # True
x < 4 or x > 8 # False
not (x == 4) # TrueLogical operators return one of the operands (see Short-circuit evaluation). In an if condition that value is just treated as truthy or falsy.
Chained Comparisons
Python lets you chain comparisons; each middle value is shared and the expression is evaluated once per comparison.
x = 6
2 < x < 10 # same as (2 < x) and (x < 10) → True
2 < x <= 6 # TrueThat’s equivalent to and-ing the comparisons. Chaining is readable and avoids repeating the variable.
Truthiness
In conditions, any value is allowed; Python treats it as truthy or falsy. That shows up a lot in interviews and in subtle bugs.
Falsy values (the condition is treated as false):
| Value | Type |
|---|---|
False | bool |
None | NoneType |
0 | int |
0.0 | float |
"" | str (empty string) |
[] | list (empty) |
() | tuple (empty) |
{} | dict (empty) |
set() | set (empty) |
Everything else is truthy: non-zero numbers, non-empty strings, non-empty collections, and most other objects.
if []:
print("list") # not run
if [2, 4]:
print("list") # runs
if "":
print("str") # not run
if "x":
print("str") # runs
if 0:
print("zero") # not run
if 2:
print("two") # runs
if None:
print("None") # not runWatch out: 0 and empty containers are falsy. if items: means “at least one”; if not items: means “empty.” Prefer that over len(items) == 0. The string "False" is truthy; only the Boolean False is falsy.
Nested Conditionals
if and elif blocks can contain more if–elif–else chains. Indentation defines which if an else belongs to; else attaches to the nearest if at the same indentation level.
x = 6
y = 4
if x > 4:
if y > 2:
print("both large")
else:
print("x large, y not")
else:
print("x not large")
# Output: both largeDeep nesting gets hard to read. elif, early returns in functions, or pulling logic into helpers usually makes the flow clearer.
Conditional Expression (Ternary)
Pick one of two values with a conditional expression: value_when_true if condition else value_when_false. It’s an expression, so it fits anywhere a value goes: assignments, arguments, return values.
x = 6
label = "even" if x % 2 == 0 else "odd" # "even"Only one branch is evaluated. Good for short, clear choices; for anything longer, a normal if–else block is easier to read.
a, b = 4, 8
max_val = a if a >= b else b # 8Short-Circuit Evaluation
and and or short-circuit: they stop as soon as the result is clear. and returns the first falsy value or the last; or returns the first truthy value or the last. So you get one of the operands back, not always True or False. In an if that’s still treated as truthy or falsy.
x = 0
y = 4
# x is falsy; (x and y) never evaluates y, result is 0
if x and y:
print("both") # not run
# x is falsy; (x or y) evaluates y, result is 4 (truthy)
if x or y:
print("at least one") # runsUsing or for a default only works when the real value is never falsy. user_input or "Anonymous" is fine. cache or 0 is wrong when 0 is a valid cached value; use cache if cache is not None else 0 (or similar) when “missing” isn’t the same as a valid value.
Style and Clarity
Returning early from a function often reads better than one deep if–elif–else. Use elif when the cases are mutually exclusive; use nested if when the inner check only applies when the outer one is true. Prefer positive conditions (if valid: instead of if not invalid:) when it’s clearer.
Tricky Behaviors
Order below matches the main content: conditions and comparisons first, then truthiness, then elif/else, then logical operators and short-circuit, then the ternary form.
= in a condition
= assigns, == compares. if x = 4 is a syntax error. Use == (or is for identity). The walrus operator := is for places where an expression is allowed (e.g. while), not for if conditions.
x = 4
if x == 6: # correct
print("six")
# if x = 6: # wrong; syntax erroris vs == for None and for integers
Use is / is not for None; style guides prefer it over ==. For numbers, use ==. is checks identity; small ints are cached in CPython so x is 4 can seem to work, but it’s not something to rely on.
Truthiness: more than True and False
Only a fixed set is falsy: False, None, 0, 0.0, "", and empty [], (), {}, set(). Everything else is truthy. The string "False" and the list [0] are truthy; “looks like a flag” doesn’t make it falsy.
Falsy isn’t False
0, "", [] are falsy in conditions but aren’t the same object as False. 0 == False is True; 0 is False is False. Prefer if items: over comparing to False for collections.
elif order: first match wins
Conditions are checked top to bottom; the first true one runs and the rest are skipped. Put the most specific or likely case first when it matters.
and / or return an operand, not always a bool
and returns the first falsy value or the last; or returns the first truthy value or the last. So 2 or 4 is 2, 0 or 4 is 4. In an if that’s fine; in assignments you get the actual value, not necessarily True/False.
Defaults with or when 0 or "" is valid
cache or 0 treats both None and 0 as “use default.” If 0 is a valid value, that’s wrong. Use cache if cache is not None else 0 (or similar) when the sentinel isn’t the same as a valid value.
Chained comparisons and evaluation order
In a < b < c, b is evaluated once. If b had side effects you’d see them once, not twice.
else on a for or while loop
Loop else runs when the loop finishes without break. It doesn’t mean “the iterable was empty.” Easy to mix up with if–else.
Conditional expression: only one branch runs
In a if cond else b, only one of a or b is evaluated. The other branch’s side effects don’t run.
Interview Questions
Order follows the main content: conditions and comparisons, then truthiness, then elif/else, then logical operators and short-circuit, then the ternary form.
What happens if you write “if x = 4”?
SyntaxError. Assignment isn’t allowed there. Use == to compare.
What is the difference between == and is?
== compares values; is compares identity (same object). Use == for numbers and most values. Use is for None; PEP 8 recommends it. For small integers, is can seem to work because of caching; stick to ==.
Why use “if x is None” instead of “if x == None”?
is means “this exact object”; == can be overridden. For None, is is clearer and preferred.
Which values are falsy in Python?
False, None, 0, 0.0, "", and empty [], (), {}, set(). Everything else is truthy. So "False" and [0] are truthy.
What does “if 0:” do? What about “if [0]:”?
if 0: is false (block skipped). if [0]: is true; the list isn’t empty, so it’s truthy. For collections, “empty or not” matters, not what’s inside.
Why is “if items:” preferred over “if len(items) > 0”?
Shorter and idiomatic. Empty collections are falsy, so if items: means “at least one.” Same for if not items: instead of len(items) == 0.
When does the else clause on a for or while loop run?
When the loop finishes without break. If you break out, the else block is skipped. So else means “completed without breaking.”
When would you use elif instead of nested if?
elif when the cases are mutually exclusive at the same level (one variable, several values). Nested if when the inner check only makes sense when the outer one is true (e.g. “if logged in, then if admin”).
What do and and or return?
One of the operands. and returns the first falsy or the last; or returns the first truthy or the last. So 2 or 4 is 2. In conditions that value is treated as truthy or falsy.
What is short-circuit evaluation?
and and or stop as soon as the result is clear. If the left of and is falsy, the right isn’t run. If the left of or is truthy, the right isn’t run. That lets you write things like x and x.method() safely.
When is “x or default” a bug?
When a valid value is falsy (0, "", []). Then x or default overwrites it. Use something like x if x is not None else default when “missing” isn’t the same as a valid value.
How do you choose between a conditional expression and an if-else block?
Use a if cond else b for short, single-value choices. Use an if–else block when a branch has several statements or is easier to read on multiple lines.
Related: match (Structural Pattern Matching)
When there are many discrete branches (e.g. several string or numeric literals), Python 3.10+ match–case can make long if–elif–else chains clearer. For two or three outcomes, if–elif–else is still the usual choice.
In practice: use if–elif–else for branching, == for values and is for None, and truthiness for “has items” or “is set.” Don’t use = in conditions, and remember loop else (runs when you don’t break) is not the same as if–else.