Booleans, Truthiness, and Short-Circuit Logic

Updated

September 7, 2026

Booleans, Truthiness, and Short-Circuit Logic

After reading this chapter, you will master Python’s bool type, the exact rules of truth value testing (truthiness), and how short-circuit evaluation powers defensive programming idioms.

Mental model

In Python, bool is an explicit subclass of int. There are only two instances: True and False.

               ┌────────────────┐
               │  class 'int'   │
               └───────┬────────┘
                       │ inherits
               ┌───────▼────────┐
               │  class 'bool'  │
               └───────┬────────┘
                       │
         ┌─────────────┴─────────────┐
         ▼                           ▼
   True (int value: 1)         False (int value: 0)

Every Python object can be evaluated in a boolean context (such as an if statement or while loop). The language defines a specific set of falsy values; every other object in Python is considered truthy.

Falsy Objects:
- Constants: None, False
- Zero numbers: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
- Empty sequences: "", (), [], b"", bytearray(b"")
- Empty collections/mappings: {}, set(), frozenset()
- Custom objects defining __bool__() returning False, or __len__() returning 0

Minimal example

Save as truthiness_demo.py:

# truthiness_demo.py
def main() -> None:
    falsy_values = [None, False, 0, 0.0, "", [], {}, set()]

    print("Checking standard falsy values:")
    for item in falsy_values:
        print(f"bool({repr(item):10s}) -> {bool(item)}")

    # Short-circuit returns the deciding operand, not necessarily a boolean!
    val1 = "fallback_host" or "default"
    val2 = "" or "fallback_host"
    print(f"\nval1: {val1}")
    print(f"val2: {val2}")

if __name__ == "__main__":
    main()

Run via uv run python truthiness_demo.py:

Checking standard falsy values:
bool(None      ) -> False
bool(False     ) -> False
bool(0         ) -> False
bool(0.0       ) -> False
bool(''        ) -> False
bool([]        ) -> False
bool({}        ) -> False
bool(set()     ) -> False

val1: fallback_host
val2: fallback_host

Worked examples

Case 1: Short-circuit evaluation as a safety guard

The and operator evaluates left-to-right; if the left operand is falsy, it immediately aborts evaluation and returns that operand. This allows writing safe guards:

# guard_demo.py
class DeviceConnection:
    def __init__(self, connected: bool) -> None:
        self.connected = connected

    def query_stats(self) -> str:
        return "CPU: 12%, Temp: 41C"

def check_device(dev: DeviceConnection | None) -> None:
    # Guard against None, then check connected attribute
    if dev is not None and dev.connected:
        print(f"Device live: {dev.query_stats()}")
    else:
        print("Device unavailable or offline")

if __name__ == "__main__":
    check_device(None)
    check_device(DeviceConnection(connected=False))
    check_device(DeviceConnection(connected=True))

Run:

uv run python guard_demo.py

Case 2: The conditional ternary expression

Python supports inline ternary expressions: value_if_true if condition else value_if_false:

# ternary_demo.py
def get_env_concurrency(is_prod: bool) -> int:
    workers = 32 if is_prod else 4
    return workers

if __name__ == "__main__":
    print(f"Development workers: {get_env_concurrency(is_prod=False)}")
    print(f"Production workers : {get_env_concurrency(is_prod=True)}")

Run:

uv run python ternary_demo.py

Pitfalls

Pitfall 1: Fallback idiom swallowing valid falsy values

# Dangerous when 0 or False is a legitimate user configuration value:
timeout = config.get("timeout") or 30  # If timeout was configured as 0, it gets replaced by 30!

# Correct fix:
timeout = config["timeout"] if "timeout" in config and config["timeout"] is not None else 30

Pitfall 2: Explicit comparison with True or False

Never write if is_ready == True: or if is_ready is True:. Idiomatic Python simply writes if is_ready:.


Exercises

  1. Create a custom class ServerPool that evaluates to False in a boolean context when its internal server list is empty, and True when it has at least one server, using __bool__().
  2. Trace the exact return value of: "" and "hello", "hello" and "world", [] or 0 or "default". Verify by running in a script.
  3. Write a validator function is_valid_payload(payload: dict) -> bool that checks if the dictionary is non-empty and contains required keys "id" and "timestamp" using short-circuit boolean logic.

Further reading

  • Python Language Reference: Section 6.11: Boolean operations.
  • Python Standard Library: Truth Value Testing.
  • PEP 285: Adding a bool type.