Boolean Short-Circuiting

and/or don't return True or False — they return one of their operands, and stop early.

Here is the fact that surprises most people: in Python, and and or do not return a Boolean. They return one of the operands themselves, and they stop evaluating as soon as the answer is known.

  • a and b → if a is falsy, return a; otherwise return b.
  • a or b → if a is truthy, return a; otherwise return b.
print(0 and 99)      # 0    — stopped at the falsy left side
print(2 and 99)      # 99   — left truthy, so returns the right
print('' or 'Guest') # Guest
print('Ada' or 'Guest')  # Ada

Why short-circuiting matters

Because the right side may never run, you can use it as a guard: user and user.is_active safely skips the attribute access when user is None. This is the same laziness that makes func() or expensive_default() avoid computing the default unless it is actually needed.

Example

Example · python
log = []

def cheap():
    log.append('cheap')
    return 0            # falsy

def pricey():
    log.append('pricey')
    return 'result'

# 'or' returns the first truthy operand — pricey() runs only because cheap() was falsy
value = cheap() or pricey()
print('value       :', value)      # result
print('what ran    :', log)         # ['cheap', 'pricey']

log.clear()
# 'and' stops at the first falsy operand — pricey() is never called
value = cheap() and pricey()
print('value       :', value)      # 0
print('what ran    :', log)         # ['cheap']  (short-circuited!)

# A safe navigation guard
def greet(user):
    name = user and user.get('name')     # None-safe: skips .get if user is None
    return f'Hi, {name or "stranger"}'

print(greet({'name': 'Ada'}))       # Hi, Ada
print(greet(None))                  # Hi, stranger

# Output:
# value       : result
# what ran    : ['cheap', 'pricey']
# value       : 0
# what ran    : ['cheap']
# Hi, Ada
# Hi, stranger

When to use it

  • A default value pattern uses 'name or "Guest"' to substitute a fallback when name is empty.
  • A guard uses 'user and user.is_active' to avoid an AttributeError when user might be None.
  • A permission check short-circuits with 'or' so expensive database lookups are skipped for admins.

More examples

and/or return operands, not booleans

Shows that 'and'/'or' return one of their operands rather than always returning True/False.

Example · python
print(0 or 'default')    # 'default'  (0 is falsy, returns right)
print(5 or 'default')    # 5  (5 is truthy, returns left)
print(None and 'value')  # None  (None is falsy, stops)
print(1 and 'value')     # 'value'  (1 is truthy, returns right)

Default value pattern

Uses the 'or' short-circuit to replace a falsy (None or empty) name with a default string.

Example · python
def greet(name=None):
    name = name or 'Guest'
    print(f'Hello, {name}!')

greet()          # Hello, Guest!
greet('Alice')   # Hello, Alice!

Safe attribute access with and

Guards against AttributeError when 'user' might be None by short-circuiting with 'and'.

Example · python
user = None

# Safe: 'and' stops at None, never accesses .name
name = user and user.name
print(name)   # None  (no AttributeError)

class User:
    name = 'Alice'
user = User()
print(user and user.name)   # Alice

Discussion

  • Be the first to comment on this lesson.