Boolean Values

True and False are the only two bool values in Python, but nearly every object can stand in for one through truth-value testing — the mechanism behind every if, while, and implicit boolean context.

Python's bool type has exactly two instances: True and False. But Python doesn't require an explicit bool in conditions — any object can be tested for truthiness, following well-defined rules for what counts as truthy and what counts as falsy. This topic covers the bool type itself, the truth-value-testing protocol, and how bool() and None fit into the picture.

True and False

True and False are the two singleton instances of the bool type, which — as covered in Built-in Data Types — is a subclass of int. This means True and False behave as 1 and 0 in arithmetic contexts, while still printing and comparing as booleans in normal use.

print(type(True))         # <class 'bool'>
print(True == 1)           # True
print(False == 0)          # True
print(True + True)          # 2 -- arithmetic works because bool IS-A int

Truth Value Testing

Any object can be used where a boolean is expected — in an if, while, and/or, or a call to bool(). Python evaluates the object's truthiness by calling its __bool__ method if defined, or falling back to __len__ (an object with __len__ is falsy if its length is 0, truthy otherwise). Objects that define neither are considered truthy by default.

class Cart:
    def __init__(self, items):
        self.items = items
    def __len__(self):
        return len(self.items)

empty_cart = Cart([])
full_cart = Cart(["apple"])
print(bool(empty_cart))   # False -- len() is 0
print(bool(full_cart))     # True  -- len() is nonzero

if empty_cart:
    print("has items")
else:
    print("cart is empty")   # this branch runs

The Falsy Values

Python defines a specific, memorizable set of built-in values that are falsy. Everything else is truthy.

Falsy values:
False
None
0        (int zero)
0.0      (float zero)
0j       (complex zero)
""       (empty string)
[]       (empty list)
()       (empty tuple)
{}       (empty dict)
set()    (empty set)

Any object whose __bool__ returns False,
or whose __len__ returns 0 (and no __bool__ is defined)
for value in (False, None, 0, 0.0, 0j, "", [], (), {}, set()):
    print(repr(value), bool(value))

bool()

bool(x) explicitly converts any object to its truthiness as a bool. It's the same evaluation Python performs implicitly in an if or while, made explicit and returning an actual True/False value you can store or pass around.

print(bool("hello"))    # True -- non-empty string
print(bool(""))          # False -- empty string
print(bool(42))           # True -- nonzero
print(bool(0))             # False -- zero
print(bool([1, 2]))        # True -- non-empty list
print(bool(None))           # False

None and Boolean Context

None is always falsy, but it's important to distinguish "falsy" from "is None." A variable that is 0, "", or [] is also falsy but is not None — conflating these can hide bugs when you specifically need to detect "no value was provided" versus "an empty/zero value was provided."

Use if x is None: when you specifically mean "no value was given"; use if not x: (or if x:) when you mean "falsy/empty in the general sense." They are not interchangeable.

def process(data=None):
    if data is None:           # specifically checks for 'not provided'
        data = []
    data.append("processed")
    return data

print(process())          # ['processed'] -- None became a fresh list
print(process([]))         # ['processed'] -- an explicitly empty list still works correctly

# Contrast with the buggy version:
def process_buggy(data=None):
    if not data:               # WRONG if the caller intentionally passes an empty list/0/""
        data = []
    ...

Truthy/Falsy Objects: Custom Classes

Any user-defined class can control its own truthiness by implementing __bool__. This is useful for domain objects that have a natural notion of "empty" or "zero" beyond simple length.

class Balance:
    def __init__(self, amount):
        self.amount = amount
    def __bool__(self):
        return self.amount != 0

zero_balance = Balance(0)
positive_balance = Balance(50)
print(bool(zero_balance), bool(positive_balance))   # False True
if positive_balance:
    print("account has funds")

Practical Usage in Conditions

Relying on truthiness rather than explicit comparisons is idiomatic Python ("Pythonic") for common emptiness/zero checks, but should be used deliberately — not as a substitute for a clear is None check when None specifically matters.

names = []
if names:                 # Pythonic: True only if the list is non-empty
    print(names[0])
else:
    print("no names yet")

count = 0
if count:                  # False when count is 0
    print("has items")
else:
    print("count is zero")

Key Takeaways

bool has exactly two instances, True and False, and is a subclass of int (True == 1, False == 0).

Truth value testing lets any object be used in a boolean context; Python calls __bool__ if defined, else falls back to __len__ == 0, else treats the object as truthy.

The full set of built-in falsy values is: False, None, 0, 0.0, 0j, "", [], (), {}, and set().

bool(x) makes truthiness evaluation explicit and returns an actual True/False.

None is always falsy, but "is falsy" and "is None" are different checks — use is None when you specifically need to detect absence of a value.

def set_count(count=None): if not count: # also true for count == 0, which may be a valid input! count = 10 return count print(set_count(0)) # 10 -- probably not what was intended def set_count(count=None): if count is None: # only true when count truly wasn't provided count = 10 return count print(set_count(0)) # 0 -- correctly preserves an explicit zero

not count treats 0, empty containers, and None identically. When zero or empty is a meaningfully different case from 'not provided', check is None explicitly.

if is_valid == True: ...

if is_valid: ...

Comparing a boolean to True/False explicitly is redundant and non-idiomatic; the value's own truthiness already expresses the check directly.

What are the two instances of bool, and what type is bool a subclass of?

True and False; bool is a subclass of int.

List all built-in falsy values.

False, None, 0, 0.0, 0j, "", [], (), {}, and set().

How does Python determine the truthiness of a custom object with no __bool__ but a __len__ method?

It falls back to __len__: the object is falsy if len(obj) == 0, truthy otherwise.

Why is if x is None different from if not x?

is None only matches the specific None singleton; not x matches None as well as any other falsy value (0, "", [], etc.), which may incorrectly conflate 'not provided' with 'empty/zero'.

What does bool("False") evaluate to, and why might that surprise a beginner?

True -- the string "False" is a non-empty string, and any non-empty string is truthy regardless of its text content.

How can a user-defined class customize its own truthiness?

By implementing a __bool__ method that returns True or False based on the object's own logic.

Is 0.0 == False true in Python? Why?

Yes -- bool is a numeric subclass of int, and 0.0 numerically equals 0, which equals False under Python's numeric equality rules.

Write a function first_truthy(items) that returns the first truthy value in a list, or None if none exist.

def first_truthy(items): for item in items: if item: return item return None print(first_truthy([0, "", None, [], 5, "x"])) # 5

Iterate and return on the first item whose truthiness is True; if the loop completes without finding one, return None.

What does this code print?

[False, True, False, True, False, True, False, True] -- each value's truthiness is evaluated independently per the falsy-value rules.

Write a class Inventory with a __bool__ method that is truthy only when its stock count is greater than zero, and demonstrate it in an if statement.

class Inventory: def __init__(self, stock): self.stock = stock def __bool__(self): return self.stock > 0 empty = Inventory(0) stocked = Inventory(5) for inv in (empty, stocked): if inv: print("in stock") else: print("out of stock")

__bool__ is invoked automatically by the if statement; it returns True only when stock > 0, so empty prints 'out of stock' and stocked prints 'in stock'.

values = [0, 1, False, True, "", "a", [], [0]]
print([bool(v) for v in values])