Python
array
length
programming
code-optimization

Is arr.__len__ the preferred way to get the length of an array in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

No — len(arr) is the preferred way to get the length of an array, list, or any sequence in Python. Calling arr.__len__() directly works but violates Python conventions. The built-in len() function is the idiomatic approach because it provides a consistent interface, includes type checking, and is recognized by every Python developer. The __len__ method exists so that custom objects can support len(), not so that you call it directly.

len() vs len()

python
1my_list = [10, 20, 30, 40, 50]
2
3# Preferred — idiomatic Python
4print(len(my_list))       # 5
5
6# Works but not recommended
7print(my_list.__len__())  # 5

Both return the same value, but len() is the standard. Python's design philosophy (documented in PEP 8) says to use built-in functions rather than calling dunder methods directly.

Why len() Is Preferred

python
1# 1. len() validates the return type
2class Bad:
3    def __len__(self):
4        return -1  # Negative length
5
6len(Bad())
7# TypeError: __len__() should return >= 0
8
9# 2. len() validates it returns an integer
10class AlsoBad:
11    def __len__(self):
12        return 3.5
13
14len(AlsoBad())
15# TypeError: 'float' object cannot be interpreted as an integer
16
17# 3. Direct __len__() skips these checks
18Bad().__len__()  # Returns -1 — no error
19AlsoBad().__len__()  # Returns 3.5 — no error

len() enforces that the result is a non-negative integer. Calling __len__() directly bypasses these safety checks. This matters when working with third-party objects that may have buggy implementations.

How len() Works Internally

python
1# Simplified version of what len() does
2def my_len(obj):
3    if not hasattr(obj, '__len__'):
4        raise TypeError(f"object of type '{type(obj).__name__}' has no len()")
5    result = obj.__len__()
6    if not isinstance(result, int):
7        raise TypeError("'float' object cannot be interpreted as an integer")
8    if result < 0:
9        raise ValueError("__len__() should return >= 0")
10    return result

In CPython, len() is implemented in C and calls __len__ through the type's slot mechanism, which is faster than a Python-level method call.

Implementing len in Custom Classes

python
1class Playlist:
2    def __init__(self):
3        self.songs = []
4
5    def add(self, song):
6        self.songs.append(song)
7
8    def __len__(self):
9        return len(self.songs)
10
11    def __repr__(self):
12        return f"Playlist({len(self)} songs)"
13
14playlist = Playlist()
15playlist.add("Song A")
16playlist.add("Song B")
17playlist.add("Song C")
18
19print(len(playlist))  # 3
20print(bool(playlist))  # True — __len__ also powers bool()

Implementing __len__ on your class enables len(obj) and also makes the object truthy/falsy based on its length. An empty container is falsy, a non-empty one is truthy.

len and Boolean Context

python
1class Queue:
2    def __init__(self):
3        self.items = []
4
5    def __len__(self):
6        return len(self.items)
7
8q = Queue()
9
10# __len__ returning 0 means the object is falsy
11if not q:
12    print("Queue is empty")  # This prints
13
14q.items.append("task")
15
16if q:
17    print("Queue has items")  # This prints

When Python evaluates if obj:, it calls bool(obj). If the class defines __len__ but not __bool__, Python uses __len__() != 0 as the boolean value.

Performance Comparison

python
1import timeit
2
3data = list(range(1000))
4
5# len() — uses C-level slot lookup
6t1 = timeit.timeit(lambda: len(data), number=1_000_000)
7
8# __len__() — Python attribute lookup + method call
9t2 = timeit.timeit(lambda: data.__len__(), number=1_000_000)
10
11print(f"len():      {t1:.3f}s")
12print(f"__len__():  {t2:.3f}s")
13# len() is typically 20-40% faster

len() is faster because CPython optimizes it as a C function that accesses the internal length field directly through the type's slot, avoiding the overhead of Python-level attribute lookup and method dispatch.

Other Dunder Methods You Should Not Call Directly

python
1x = [1, 2, 3]
2
3# Don't do this          # Do this instead
4x.__len__()              # len(x)
5x.__getitem__(0)         # x[0]
6x.__contains__(2)        # 2 in x
7x.__str__()              # str(x)
8x.__repr__()             # repr(x)
9x.__iter__()             # iter(x)
10x.__next__()             # next(x)

The pattern is consistent across Python — dunder methods are implementation hooks, not part of the public API. Use the corresponding built-in function or operator.

Common Pitfalls

  • Calling len() directly in production code: It bypasses validation, is harder to read, and is slower. Always use len().
  • Forgetting len returns int: If your custom __len__ returns a float or string, len() raises TypeError. Always return an integer.
  • Confusing len() with sys.getsizeof(): len([1, 2, 3]) returns 3 (number of elements). sys.getsizeof([1, 2, 3]) returns the memory footprint in bytes. They measure different things.
  • Assuming len() is O(1) for all types: len() is O(1) for lists, tuples, dicts, sets, and strings because they store their length internally. Custom classes could implement __len__ as O(n) — the cost depends on the implementation.
  • Not implementing len for container classes: If your class represents a collection, implement __len__ so it works with len(), bool(), and other Python idioms. Without it, len(obj) raises TypeError.

Summary

  • Always use len(obj) instead of obj.__len__() — it is the Pythonic way
  • len() validates the return value (must be a non-negative integer)
  • len() is faster than __len__() due to C-level optimizations in CPython
  • Implement __len__ in custom classes to support len() and boolean evaluation
  • __len__ returning 0 makes the object falsy — this powers if not container: patterns
  • Never call dunder methods directly — use the corresponding built-in function or operator

Course illustration
Course illustration

All Rights Reserved.