Booleans & Truthiness

Reviewed & published by Brayan K

Truthiness is Python's rule that every value is treated as either "truthy" or "falsy" in a boolean context, so any object — not just True and False — can control an if or while.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Understanding which values count as falsy, and how and/or actually behave, unlocks some of the cleanest, most readable patterns in the language.

What You'll Learn in This Lesson

1 bool() and the Falsy Values

Pass any object to bool() and Python tells you whether it's truthy or falsy. There's a short, fixed list of falsy things — everything else is truthy:

# The falsy values
print(bool(False))   # False
print(bool(None))    # False
print(bool(0))       # False   (also 0.0)
print(bool(""))      # False   (empty string)
print(bool([]))      # False   (empty list)
print(bool({}))      # False   (empty dict)

# Everything else is truthy
print(bool(1))       # True
print(bool(-5))      # True   (any non-zero number)
print(bool("0"))     # True   (a non-empty string!)
print(bool([0]))     # True   (a list with one item)
# Test the truthiness of different values
values = [0, 1, "", "hi", [], [1], None, "0"]

for v in values:
    print(repr(v), "->", bool(v))

# ✅ Expected output:
# 0 -> False
# 1 -> True
# '' -> False
# 'hi' -> True
# [] -> False
# [1] -> True
# None -> False
# '0' -> True

✅ Truthiness in if Statements

Because empty collections are falsy, you can test them directly. This is more readable and more idiomatic than comparing lengths:

cart = []

# The Pythonic way
if cart:
    print("Checkout", len(cart), "items")
else:
    print("Your cart is empty")     # this runs

# Works for strings too
name = ""
if not name:
    print("Please enter a name")    # this runs

# Compare: the clunky, non-idiomatic version
if len(cart) > 0:        # works, but verbose
    print("has items")

Prefer if cart: over if len(cart) > 0: and if not name: over if name == "":. Shorter, clearer, and it works for every kind of collection.

2 and / or Return Operands (Short-Circuit)

This surprises newcomers: and and or don't return True/False — they return one of the actual operands, and they stop early ("short-circuit") as soon as the answer is known:

# 'or' returns the first TRUTHY operand (or the last one)
print(0 or "fallback")     # 'fallback'
print("Ada" or "Guest")    # 'Ada'  -> stops at the first truthy
print(0 or "" or "last")   # 'last'

# 'and' returns the first FALSY operand (or the last one)
print("a" and "b")         # 'b'  -> both truthy, returns the last
print(0 and "b")           # 0    -> stops at the first falsy

# Common default pattern
username = ""
display = username or "Anonymous"
print(display)             # 'Anonymous'
# and / or return operands and short-circuit
def get_name(raw):
    # If raw is empty/None, fall back to a default
    return raw or "Anonymous"

print(get_name("Grace"))   # Grace
print(get_name(""))        # Anonymous
print(get_name(None))      # Anonymous

# Short-circuit avoids an error: the right side is skipped
data = None
print(data and data["key"])   # None  -> never indexes None

# 'and' returns the last value when all are truthy
print(1 and 2 and 3)          # 3

# ✅ Expected output:
# Grace
# Anonymous
# Anonymous
# None
# 3

🧩 Checking Many Items with any() & all()

any() asks "is at least one truthy?" and all() asks "are they all truthy?". Pair them with a generator expression for clean validation:

scores = [85, 92, 78, 90]

# Are ALL scores passing (>= 60)?
print(all(s >= 60 for s in scores))   # True

# Is ANY score perfect (== 100)?
print(any(s == 100 for s in scores))  # False

# Validate a form: every field filled?
fields = ["Ada", "ada@x.com", ""]
print(all(fields))                    # False  ('' is falsy)

# The empty-iterable edge cases
print(any([]))    # False  -> nothing is truthy
print(all([]))    # True   -> nothing fails

all([]) being True reads as "no item broke the rule", and any([]) being False reads as "no item satisfied it". Keep that edge case in mind for empty inputs.

🎯 Your Turn: Fill in the Blanks

Replace each ___ with any, all, or, or not so the output matches.

# 🎯 YOUR TURN: replace each ___ to match the expected output
ages = [25, 17, 40, 30]
name = ""

# 1. Is EVERYONE an adult (>= 18)?  -> False
print(___(a >= 18 for a in ages))

# 2. Is ANYONE a minor (< 18)?  -> True
print(___(a < 18 for a in ages))

# 3. Give a default name -> 'Guest'
print(name ___ "Guest")

# 4. Is the name missing?  -> True
print(___ name)

# Expected output:
# False
# True
# Guest
# True
print(all(a >= 18 for a in ages))   # False
print(any(a < 18 for a in ages))    # True
print(name or "Guest")              # Guest
print(not name)                     # True

Common Errors & How to Fix Them

❌ Assuming the string "False" is falsy

if "False":     # this is TRUE - it's a non-empty string!
    print("runs")

✅ Only the empty string "" is falsy. Compare values explicitly (flag == "False") or convert properly.

❌ Using 'or' for defaults when 0 is valid

qty = 0
result = qty or 1     # gives 1, but 0 was a real value!

✅ 0 is falsy, so or discards it. When zero/empty is legitimate, test is None instead: qty if qty is not None else 1.

❌ Forgetting all([]) is True

items = []
if all(x > 0 for x in items):
    print("all positive")    # this RUNS on empty input

✅ all() of an empty iterable is True. Guard with if items and all(...) when emptiness should fail.

🎯 Mini Challenge: Form Validator

Build a tiny validator that uses truthiness, all(), and an or default.

# Mini Challenge: validate a signup form
def validate(form):
    required = [form.get("name"), form.get("email"), form.get("pw")]
    is_valid = all(required)             # every field non-empty?
    display = form.get("name") or "New User"
    status = "OK" if is_valid else "MISSING FIELDS"
    return display, status

print(validate({"name": "Ada", "email": "a@x.com", "pw": "secret"}))
print(validate({"name": "", "email": "b@x.com", "pw": "pw"}))

# Expected output:
# ('Ada', 'OK')
# ('New User', 'MISSING FIELDS')

📚 Keep Learning

Go deeper with the official Python documentation:

Frequently Asked Questions

Lesson complete — truthiness demystified!

You know the falsy values cold, you can test collections directly with if items:, you understand that and/or return operands and short-circuit, and you can validate with any() and all().

Practice quiz

Which of these is a FALSY value in Python?

  • The string '0'
  • The list [0]
  • An empty list []
  • The string 'False'

Answer: An empty list []. An empty list [] is falsy. The string '0', the list [0], and the string 'False' are all non-empty, so they are truthy.

What does bool('0') return?

  • False
  • True
  • 0
  • None

Answer: True. '0' is a non-empty string, so it is truthy — bool('0') is True. Only the empty string '' is falsy.

What does the expression 0 or 'fallback' evaluate to?

  • True
  • 0
  • 'fallback'
  • False

Answer: 'fallback'. or returns the first truthy operand; 0 is falsy, so it returns 'fallback'. This is the common default-value pattern.

What does 'a' and 'b' evaluate to?

  • True
  • 'a'
  • 'b'
  • False

Answer: 'b'. and returns the first falsy operand, or the last one if all are truthy. Both are truthy, so it returns the last: 'b'.

What does 0 and 'b' evaluate to?

  • 0
  • 'b'
  • True
  • False

Answer: 0. and short-circuits at the first falsy operand and returns it. 0 is falsy, so it returns 0 without evaluating 'b'.

What is the result of any([]) on an empty list?

  • True
  • False
  • None
  • It raises an error

Answer: False. any([]) is False — with no items, nothing is truthy, so any() returns False.

What is the result of all([]) on an empty list?

  • True
  • False
  • None
  • It raises an error

Answer: True. all([]) is True — with no items, nothing breaks the rule, so all() vacuously returns True.

Why can 'qty or 1' be a bug when qty is 0?

  • 0 raises a TypeError
  • 0 is falsy, so or discards it and returns 1 even though 0 was a real value
  • or only works on strings
  • It always returns True

Answer: 0 is falsy, so or discards it and returns 1 even though 0 was a real value. 0 is falsy, so 'qty or 1' gives 1 even when 0 is a legitimate value. Use 'qty if qty is not None else 1' instead.

What is the Pythonic way to check that a list 'cart' is non-empty?

  • if len(cart) > 0:
  • if cart != []:
  • if cart:
  • if bool(cart) == True:

Answer: if cart:. Because empty collections are falsy, 'if cart:' is the idiomatic, readable test for a non-empty collection.

What does 1 and 2 and 3 evaluate to?

  • 1
  • True
  • 3
  • 6

Answer: 3. When every operand is truthy, and returns the LAST one, so 1 and 2 and 3 evaluates to 3.

Continue this course

Frequently asked questions

What exactly is 'truthiness' in Python?

Truthiness is the idea that every object has an inherent boolean value, even if it isn't a True or False. When you put any object in an if or while, Python asks 'is this truthy?' Empty collections, the number zero, empty strings, and None are falsy; almost everything else is truthy. This lets you write 'if items:' instead of 'if len(items) > 0:'.

Which values are falsy?

The standard falsy values are: False, None, zero of any numeric type (0, 0.0, 0j), and every empty container — empty string '', empty list [], empty tuple (), empty dict {}, and empty set. Everything else is truthy, including the string 'False', the string '0', and the list [0]. If it isn't on the falsy list, it's truthy.

Why do 'and' and 'or' sometimes return a value instead of True/False?

Because Python's and/or return one of the original operands, not a boolean. 'a or b' returns a if a is truthy, otherwise b — so 'name or "Guest"' gives a default. 'a and b' returns a if a is falsy, otherwise b. They also short-circuit: the second operand is only evaluated if needed, which lets you guard against errors.

When should I use any() and all()?

Use any(iterable) to ask 'is at least one item truthy?' and all(iterable) to ask 'are they ALL truthy?'. They short-circuit (any stops at the first truthy, all stops at the first falsy) and pair perfectly with a generator expression, like all(x > 0 for x in nums). Note any([]) is False and all([]) is True — the empty-case edge that trips people up.