5.11. Conditionals¶
A conditional runs a block of code only when some test
evaluates to true. The keyword is if, optionally followed by
one or more elif (“else if”) branches and a final else.
n = 42
if n > 0:
print("positive")
elif n < 0:
print("negative")
else:
print("zero")
The body of each branch is everything indented under it (four
spaces by convention). Python walks the branches in order, runs
the first one whose test is true, and skips the rest. The
else block runs only if every preceding test was false; it
is always optional.
Only one branch ever runs. Tests are evaluated top to bottom until one succeeds; the rest are skipped.¶
5.11.1. Truthiness¶
A test in an if does not have to return True or
False – any value counts as either truthy or falsy.
The falsy values are:
Everything else is truthy. This lets you write compact tests:
if name: # false on empty string
print("hello", name)
if items: # false on empty list, dict, etc.
process(items)
Be aware that truthiness changes the meaning. if value: is
not the same as if value is not None: – the first one is
also false when value is 0 or "". When you really
mean “is this exactly None”, use is None /
is not None explicitly.
5.11.2. Ternary expressions¶
A conditional can appear inside an expression:
label = "even" if n % 2 == 0 else "odd"
Read as “label is "even" if n % 2 == 0 else
"odd".” Handy for one-liners; for anything more than one
line, a full if statement is easier to read.
5.11.3. Nesting and early returns¶
Conditionals can nest arbitrarily deep, but every extra layer of indentation makes a function harder to read. The example below checks four conditions before doing the real work and leaves the useful line buried four indents in:
def process(item):
if item is not None:
if item.is_valid():
if item.size() > 0:
if item.owner == "me":
return do_the_work(item)
return None
Two patterns flatten this kind of code.
5.11.3.1. Early returns for guards¶
Handle every “bail out” case first, each with its own return,
so the main logic stays at the outer indent. Each guard reads as
“this is not a case we handle; leave”:
def process(item):
if item is None:
return None
if not item.is_valid():
return None
if item.size() == 0:
return None
if item.owner != "me":
return None
return do_the_work(item)
The “main path” is now one line at the bottom of the function, not buried inside four layers. This style is sometimes called the guard clause pattern.
5.11.3.2. Combining tests with and / or¶
When several conditions all have to hold for the same branch,
combine them with and rather than nesting. Conditions that
each independently trigger the branch combine with or:
# all must hold -- use `and`
if user.is_admin() and user.has_permission("write") and not locked:
save()
# any one of them is enough -- use `or`
if c == " " or c == "\t" or c == "\n":
whitespace_count += 1
Both forms short-circuit, so an expensive check on the right side only runs when the cheaper checks on the left have not already settled the question.