Python Truth Value Testing: bool vs len
In Python, every object can be evaluated in a Boolean context, such
as within an if or while statement. This
article explains the underlying mechanism Python uses to determine
truthiness, specifically detailing the priority order between the
__bool__() and __len__() special methods, the
fallback behavior when neither is implemented, and the strict
constraints applied to their return values.
The Truth Testing Protocol
When Python evaluates an object x in a Boolean context,
it implicitly calls the built-in bool(x) constructor. Under
the hood, Python checks for specific magic methods in a strict
hierarchy:
__bool__()is checked first: If the object’s class defines__bool__(), Python invokes it directly to determine whether the object isTrueorFalse.__len__()acts as a fallback: If__bool__()is not implemented, Python checks for__len__(). If present, Python calls__len__(). The object is evaluated asTrueif the result is non-zero, andFalseif the result is0.- Default to
True: If neither__bool__()nor__len__()is defined on the class, the object is considered truthy by default.
__bool__ Takes
Absolute Precedence
When both methods are defined on an object, Python always calls
__bool__() and completely ignores
__len__().
class AmbiguousContainer:
def __init__(self, items):
self.items = items
def __bool__(self):
return False
def __len__(self):
return len(self.items)
container = AmbiguousContainer([1, 2, 3])
print(len(container)) # Outputs: 3
print(bool(container)) # Outputs: FalseEven though the container holds three elements and returns a length
of 3, the explicit __bool__() method forces
the object to evaluate to False.
Falling Back to
__len__
Standard collection types—such as lists, dictionaries, tuples, and
sets—do not define __bool__(). Instead, they rely on
__len__() to evaluate truthiness.
class CustomList:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
empty_list = CustomList([])
populated_list = CustomList([1, 2])
print(bool(empty_list)) # Outputs: False (len is 0)
print(bool(populated_list)) # Outputs: True (len is 2)Because __bool__() is absent, Python looks to
__len__(). A length of zero designates a falsy object,
whereas any non-zero integer evaluates to True.
Default Behavior for Custom Classes
Instances of custom classes that implement neither method are always considered truthy:
class Item:
pass
item = Item()
print(bool(item)) # Outputs: TrueThis ensures that distinct object references evaluate to
True unless the developer explicitly provides a logic for
emptiness or negation.
Type Constraints and Common Errors
Both methods enforce strict type constraints within Python:
__bool__()must return abool: Returning any type other thanTrueorFalse(such as an integer or string) raises aTypeError.__len__()must return an integer: The return value of__len__()must be a non-negative integer (>= 0). Returning a negative number raises aValueError, and returning a non-integer raises aTypeError.
class InvalidBool:
def __bool__(self):
return 1 # Raises TypeError: __bool__ should return bool, returned int
class InvalidLen:
def __len__(self):
return -5 # Raises ValueError: __len__() should return >= 0By following this two-tier resolution process, Python maintains consistent behavior across all built-in collections while offering developers fine-grained control over how custom objects behave in conditional statements.