Friday, June 5, 2026

How does Python’s handling of len() differ for built-in types vs. custom classes?

All Questions From This Chapter    « Previously

len() behaves similarly at the language level for both built-in types and custom classes — it looks for a special method called __len__() — but there are important implementation differences.

1. Built-in types: optimized, implemented in C

For built-in objects like list, str, tuple, dict, etc., length is usually:

  • Stored internally as metadata

  • Implemented in C for speed

  • Retrieved in O(1) time

Example:

Python
a = [1, 2, 3]
print(len(a)) # 3

Internally, Python does not iterate through the list each time. The list object already stores its size.


2. Custom classes: you provide __len__()

For your own classes, len(obj) calls:

Python
obj.__len__()

Example:

Python
class MyCollection:
def __init__(self, items):
self.items = items

def __len__(self):
return len(self.items)

c = MyCollection([10, 20, 30])
print(len(c)) # 3

Equivalent behavior:

Python
print(c.__len__())

3. If __len__() is missing

Python raises a TypeError.

Python
class A:
pass

a = A()
print(len(a))

Output:

TypeError: object of type 'A' has no len()

4. Rules for __len__()

Your __len__() method must return:

  • an integer

  • ≥ 0

Bad examples:

Python
class Bad:
def __len__(self):
return -1

len(Bad())

Output:

ValueError: __len__() should return >= 0

Or:

Python
class Bad:
def __len__(self):
return "five"

Output:

TypeError

5. Relationship with truthiness

If __bool__() is absent, Python uses __len__():

Python
class Box:
def __len__(self):
return 0

b = Box()

print(bool(b)) # False

Rule:

__bool__() → preferred
otherwise __len__()
otherwise True

6. Important internal difference: special method lookup

For special methods like __len__, Python does not always use normal instance attribute lookup.

This fails:

Python
class A:
pass

a = A()
a.__len__ = lambda: 5

len(a) # Error

Because Python looks up __len__ on the class, not the instance.

This works:

Python
class A:
def __len__(self):
return 5

a = A()
print(len(a)) # 5

Summary

AspectBuilt-in TypesCustom Classes
Where length logic livesC implementationYour __len__()
PerformanceUsually O(1)Depends on implementation
Need to define length?Already definedMust implement __len__()
Missing length behaviorRareTypeError
Truthiness fallbackUses internal lengthUses your __len__()

The key takeaway: len(x) always tries the object's length protocol (__len__), but built-in types have highly optimized internal implementations while custom classes must explicitly provide that behavior.


All Questions From This Chapter    « Previously

How does Python determine the truth value of an object if __bool__ is not implemented?

All Questions From This Chapter    « Previously    Next »

When Python needs to determine if an object is “truthy” or “falsy” (for example in an if statement or bool(obj)), it follows a specific fallback order:

  1. __bool__ – Python first looks for a __bool__ method on the object. If present, it calls that method and uses the boolean result directly.

  2. __len__ – If __bool__ is not implemented, Python next looks for a __len__ method. If that exists, it calls __len__() and treats the object as True if the result is non-zero, and False if the result is 0.

  3. Default True – If neither __bool__ nor __len__ is defined, the object is considered True by default. (This applies to most user-defined class instances unless they explicitly define one of the two methods.)

Thus, the truth value is False only if:

  • The object defines __bool__ and it returns False, or

  • The object does not define __bool__ but defines __len__ and __len__() returns 0.

Otherwise, it is True.

Example with a custom class

python
Copy
Download
class AlwaysTrue:
    pass                    # no __bool__, no __len__

class EmptyContainer:
    def __len__(self):
        return 0

class NonEmptyContainer:
    def __len__(self):
        return 5

class ExplicitFalse:
    def __bool__(self):
        return False

# Testing
print(bool(AlwaysTrue()))       # True (default)
print(bool(EmptyContainer()))   # False (__len__ returns 0)
print(bool(NonEmptyContainer()))# True (__len__ returns 5)
print(bool(ExplicitFalse()))    # False (__bool__ returns False)

Why this order?

It allows built-in types like lists, strings, and dicts to be “falsy” when empty without needing a special __bool__. They just implement __len__:

python
Copy
Download
bool([])      # False – list.__len__ returns 0
bool([1,2])   # True  – list.__len__ returns 2

Objects that want to be unconditionally True can leave both methods undefined. If you want to make an object explicitly falsy, define __bool__ to return False.

This mechanism is defined in Python’s Truth Value Testing rules.


All Questions From This Chapter    « Previously    Next »

What is the difference between __mul__ and __rmul__?

All Questions From This Chapter    « Previously    Next »

In Python, __mul__ and __rmul__ are both used for the * operator, but they are called in different situations.

__mul__(self, other) is tried when your object is on the left side:

Python
a * b

__rmul__(self, other) is tried when your object is on the right side and the left object does not know how to multiply with it:

Python
b * a

Example:

Python
class Box:
def __mul__(self, other):
return "Box __mul__"

def __rmul__(self, other):
return "Box __rmul__"

x = Box()

print(x * 3) # calls __mul__
print(3 * x) # calls __rmul__

Python’s rule is roughly:

  1. Try left operand’s __mul__

  2. If it returns NotImplemented, try right operand’s __rmul__

This is especially useful for supporting expressions where your custom class appears on the right side of a built-in type, like 2 * custom_object.


All Questions From This Chapter    « Previously    Next »