Python Control Flow: if-elif-else
Python if-elif-else Interview Questions
not x treats empty containers, zero, None, and False as falsey. x is False only matches the singleton False—stricter and rarely what you want unless enforcing booleans.
First truthy operand, else last (possibly non-boolean). Example: 0 or 5 → 5. Useful defaults: name = user or "guest".
Assignment is a statement, not an expression (until Python 3.8+ walrus := in limited contexts). Classic syntax error—use == for comparison or walrus inside parentheses where allowed.
If no branch matches and no else, nothing runs—often a silent bug. Cover residual cases explicitly or assert exhaustiveness for enums.
elif stops after first match—sequential ifs might evaluate multiple branches unintentionally and waste work.
Falsy like zero floats—Decimal normalizes zero to falsey context. Always understand custom numeric types’ __bool__ behavior.
Rounding noise: use math.isclose or tolerances. Equality checks on computed floats often fail unexpectedly.
Flatten with guards (if not valid: return), combine booleans, or extract helpers. Deep nesting hides bugs and hurts testing.
Names in patterns bind—order matters; use literals vs guards (case x if ...) to avoid accidental rebinding. Study PEP 634 patterns carefully.
Same list reused across calls—conditions depending on seen become wrong. Use None sentinel and assign inside.
No—short-circuit skips B. Don’t rely on side effects in B when A might succeed.
Only one branch executes—a or b is chosen after cond evaluates. Unlike C’s comma tricks, cleaner for simple assignments.
in checks keys only; falsy values stored require distinguishing missing vs falsey—use key in dict plus value check or dict.get(key, sentinel).
Exceptions should be exceptional—don’t use try/except for normal branching (slow, hides bugs). Prefer explicit tests like presence checks.
Python can strip asserts with -O—never rely on assertions for security or essential validation; use real checks.
Middle operand reused—still evaluated once per semantics but expressed twice conceptually; identical to (x==y) and (y==z).
Cleaner union type checks—one branch handles multiple acceptable classes without duplication.
# Preferred: single branch for several types
if isinstance(x, (A, B)):
handle_union(x)
# Verbose: duplicated bodies or repeated logic
if isinstance(x, A):
...
elif isinstance(x, B):
...Comparing to True is redundant and can mis-handle truthy values that are not the bool True.
# Avoid
if flag == True:
...
# Prefer (idiomatic truthiness)
if flag:
...Worse: code like if flag is True only passes for the exact singleton; truthy values such as 1 behave differently under == True vs plain if flag.
Raw literals keep backslashes single in source—easier to read and harder to get wrong when building regex strings.
import re
pattern = r"\d+" # one backslash in source → \d in regex
# vs
pattern = "\\d+" # same effect, easier to miscount slashesUnreachable—linters flag it. Python doesn’t warn at runtime; testing misses behaviors hidden below returns.
Both valid—sometimes mid-loop checks need break; avoid redundant flags that obscure exit conditions.
Second check redundant unless x mutated—signals refactor opportunity or logic bug.
Default unicode compares code points—not linguistically equal. Use locale or casefold() for user-facing equality rules.
Prefer polymorphism or singledispatch—long type-if chains resist extension and violate open/closed principle.
False-like when running with -O—gate expensive diagnostic branches without runtime cost in optimized builds.
Named booleans (valid_age = ...) communicate intent—reduces mis-parenthesizing in interviews and reviews.
CPython order is predictable but side-effectful filters are fragile—keep comprehensions pure for clarity.
in list is O(n); convert to set once if repeated checks—conditional-heavy loops bottleneck here.
Yes—means a < b and b > c; useful for “b is strictly between” style checks without repeating b.