How Python Computes the Boolean Value of Objects
Default Truthiness for Instances Without Special Methods
Objects that implement neither __bool__ nor __len__ always evaluate as True in Boolean contexts.
class Packet:
pass
pkt = Packet()
print(bool(pkt)) # True
Controlling Evaluasion with __bool__
If a class defines __bool__, that method's return value determines the instance's truthiness. Python rqeuires an actual bool return type; otherwise it raises TypeError.
class RetryLimiter:
def __init__(self, attempts):
self.attempts = attempts
def __bool__(self):
print(f"__bool__ invoked: attempts={self.attempts}")
self.attempts -= 1
return self.attempts >= 0
limiter = RetryLimiter(4)
while limiter:
print("Attempt in progress")
print("No retries left")
# __bool__ invoked: attempts=4
# Attempt in progress
# __bool__ invoked: attempts=3
# Attempt in progress
# __bool__ invoked: attempts=2
# Attempt in progress
# __bool__ invoked: attempts=1
# Attempt in progress
# __bool__ invoked: attempts=0
# No retries left
Flalback to __len__
When __bool__ is missing, the interpreter invokes __len__ if available. A return value of 0 produces False, while any non-zero integer produces True. Like __bool__, this method must return an integer.
class TaskQueue:
def __init__(self, pending):
self.pending = pending
def __len__(self):
print(f"__len__ invoked: pending={self.pending}")
self.pending -= 1
return self.pending
queue = TaskQueue(4)
while queue:
print("Processing task")
print("Queue empty")
# __len__ invoked: pending=4
# Processing task
# __len__ invoked: pending=3
# Processing task
# __len__ invoked: pending=2
# Processing task
# __len__ invoked: pending=1
# Queue empty
Priority of __bool__ Over __len__
If both methods exist, __bool__ takes precedence and __len__ is ignored during truthiness checks.
class Resource:
def __init__(self, value):
self.value = value
def __bool__(self):
print(f"__bool__ path: value={self.value}")
self.value -= 1
return self.value >= 0
def __len__(self):
print(f"__len__ path: value={self.value}")
self.value -= 1
return self.value
res = Resource(4)
while res:
print("Resource available")
print("Resource depleted")
# __bool__ path: value=4
# Resource available
# __bool__ path: value=3
# Resource available
# __bool__ path: value=2
# Resource available
# __bool__ path: value=1
# Resource available
# __bool__ path: value=0
# Resource depleted