17. Boolean logic in depth

Chapter 16 used and, or, and not to join conditions. They do more than that: knowing what they really return unlocks tricks that Python code leans on constantly. This chapter goes deeper into truth.

Truthy and falsy, again

Recall from Chapter 16: in a condition, False, None, 0, 0.0, "", and empty collections count as false. Everything else is true. Python calls those falsy and the rest truthy.

if 0:
    print("zero is truthy")     # does NOT print — 0 is falsy in Python
if "":
    print("empty is truthy")    # does NOT print — "" is falsy in Python
if None:
    print("never")              # does not print
if 1:
    print("one is truthy")      # prints
if "hi":
    print("non-empty is truthy")# prints

In Python, 0 and "" are falsy. Keep this in mind whenever you test a value directly.

and and or hand back a value

Here is what beginners rarely get told: and and or do not always return True or False. They return one of their two sides, unchanged.

  • a and b — if a is falsy, it returns a; otherwise it returns b.
  • a or b — if a is truthy, it returns a; otherwise it returns b.
print(5 and 10)          # 10    (5 is truthy, so the second value)
print(None and 10)       # None  (first is falsy, returned as-is)
print(False or "hi")     # hi    (first falsy, so the second)
print("yes" or "no")     # yes   (first truthy, returned at once)

Used in an if, this behaves like plain true/false logic — but the returned value is what makes the tricks below possible.

The default-value trick: x or default

Because or returns its first truthy side, you can supply a fallback for a value that might be None. Picture a name that may be missing:

typed_name = None                  # nothing was entered
name = typed_name or "stranger"
print("Hello, " + name)            # Hello, stranger

If typed_name had held a real value, name would keep it instead. This x or default line is a common Python way to fall back to a default — you will see it when a value might be missing.

Python 3.8 added the walrus operator := and there is also the pattern value if value is not None else default, but x or default is shorter and worth knowing for simple cases where any falsy value should trigger the fallback.

Open exercises/17/01-default.py. chosen is set to None. Use chosen or "rock" to fall back to "rock", then print it. Set chosen to a real word and run again.

Short-circuit: the second side is skipped

and and or are lazy. They stop as soon as the answer is certain:

  • a and b — if a is falsy, b is never even looked at.
  • a or b — if a is truthy, b is never looked at.

This lets the first test guard the second, which runs only after the first has passed:

total = 90
count = 0
# the average is only worth working out when count is not zero,
# so the count check guards the division
if count > 0 and total / count > 20:
    print("high average")
else:
    print("no average to show")

Because count > 0 is False, Python never evaluates total / count — it short-circuits straight to the else. The pattern if x > 0 and something-using-x shows up constantly.

not flips truthiness

not turns any value into a real boolean — True if it was falsy, False if it was truthy:

print(not None)     # True
print(not 0)        # True   (0 is falsy, so "not 0" is True)
print(not False)    # True
print(not 1)        # False  (1 is truthy)

It is handy for asking "is this missing?":

key = None
if not key:
    print("You need a key.")

Homework

Homework files are in exercises/17/homework/.

Problem 1 — Default colour

Open exercises/17/homework/01-default-colour.py. fav holds a colour or None. Using or, set colour to fav, or to "blue" when fav is None, then print Your colour is <colour>. Run it once with fav = None and once with a real colour.

Problem 2 — Truthy table

Open exercises/17/homework/02-truthy.py. For each of these values, print the value and whether it is truthy, using bool(value) to convert it to a real boolean: 0, "", None, False, "hi", 1. The bool() function gives True for truthy values and False for falsy ones.

Problem 3 — Guarded division

Open exercises/17/homework/03-guarded.py. Two numbers: total and count (which might be 0). Using and, print the average total / count only when count is greater than 0; otherwise print no data. Test it with count set to 0 and to a real number.

Challenge — First value that exists

Open exercises/17/homework/04-first-value.py. Three variables a, b, c each hold a string or None. In one line using or, print the first one with a value, or none if all three are None. Try different combinations of values and None.

Stuck or finished? Open the homework solutions page.