Back to Blog
Python

How the Python len Function Works

python len function: Understand how Python's len() works internally, which types support it, how to implement __len__ in custom classes, and why it runs in constant time.

pythonlen functionbuilt-in functionsdunder methodspython performance
Illustration showing a Python container with a visible length counter, representing how the len() function returns the number of items.

The python len function is a built-in that returns the number of items in a container. It is called with a single argument, and its behavior is defined by the object's __len__ method rather than by the function itself.

numbers = [10, 20, 30] print(len(numbers)) # 3 name = "python" print(len(name)) # 6

len() works on lists, tuples, strings, bytes, dictionaries, sets, and any object that implements the __len__ protocol. When you call len(obj), Python internally invokes obj.__len__() and returns the result as an integer.

How len() Resolves to len

The built-in len() does not inspect the object directly. It delegates to the object's __len__ method through the Python data model. This means the behavior of len() is entirely determined by how the target class implements __len__.

class ShoppingCart: def __init__(self): self.items = [] def add(self, item): self.items.append(item) def __len__(self): return len(self.items) cart = ShoppingCart() cart.add("keyboard") cart.add("mouse") print(len(cart)) # 2

When len(cart) executes, Python looks up __len__ on the ShoppingCart class and calls it. The return value must be a non-negative integer. If __len__ returns a non-integer, Python raises a TypeError.

Types That Support len()

The following built-in types implement __len__:

TypeReturns
listnumber of elements
tuplenumber of elements
strnumber of Unicode code points
bytesnumber of bytes
dictnumber of key-value pairs
setnumber of elements
rangenumber of values in the range

Note that str length is measured in Unicode code points, not necessarily in characters as perceived by a user. A grapheme such as an emoji with a combining mark can occupy multiple code points, so len() may return a value larger than the visible character count.

text = "e\u0301" # e followed by combining acute accent print(len(text)) # 2

This distinction matters when validating user input for display or storage limits.

Implementing len in Custom Classes

If you define a class that represents a collection, implementing __len__ makes it work with len(). This is useful for classes that wrap an internal sequence or that need to report a logical size.

class Playlist: def __init__(self, tracks): self._tracks = list(tracks) def __len__(self): return len(self._tracks) def __getitem__(self, index): return self._tracks[index]

Once __len__ is defined, the object also becomes compatible with the collections.abc.Sized interface. This allows code that checks isinstance(obj, Sized) to accept your class, which is useful in libraries that validate input types.

The __len__ method must return an integer greater than or equal to zero. Returning a negative value or a non-integer raises TypeError. If the logical size of your object is expensive to compute, consider whether __len__ is the right interface, because some code paths assume it is cheap.

Why len() Is O(1) for Built-in Containers

For built-in types such as list, str, dict, and set, len() runs in constant time. These containers store their size as an internal field that is updated on every mutation. Calling len() reads that field; it does not iterate over the elements.

large_list = list(range(1_000_000)) print(len(large_list)) # instant, regardless of size

This is why checking if len(items) == 0 is a constant-time operation for a list. It is not scanning the list to count elements. The same is true for strings, dictionaries, and sets.

For custom classes, the complexity of len() depends entirely on the implementation of __len__. If your __len__ iterates over a collection to count elements, then len() becomes O(n) for that class. This can surprise code that calls len() repeatedly in a loop.

class SlowCounter: def __init__(self, data): self.data = data def __len__(self): return sum(1 for _ in self.data) # O(n) on every call

If a library calls len() on this object multiple times, the cost multiplies. Prefer storing the size explicitly when the class represents a collection whose size changes infrequently.

Common Mistakes and Edge Cases

One common mistake is assuming len() works on any iterable. Generators and iterators do not implement __len__ because their size is not known without consuming them.

gen = (x for x in range(10)) # len(gen) # TypeError: object of type 'generator' has no len()

If you need the length of a generator, you must consume it and count the items, which changes the state of the generator. In practice, this means you should decide before iteration whether you need the count.

Another edge case is len() on None or on an integer. These types do not define __len__, so calling len(None) raises a TypeError. The error message identifies the type, which helps during debugging:

# len(None) # TypeError: object of type 'NoneType' has no len()

When validating input, check for the presence of __len__ with hasattr(obj, "__len__") before calling len() if the input type is not guaranteed.

Using len() for Emptiness Checks

For built-in containers, len(obj) == 0 and not obj are equivalent, but not obj is the more common idiom in Python. Both are O(1) for lists, strings, dicts, and sets. The choice is stylistic; not obj is shorter and is widely used in conditionals.

items = [] if not items: print("no items")

This works because empty containers evaluate to False in a boolean context. For custom classes, this behavior is controlled by __bool__, not __len__. If a class defines __len__ but not __bool__, Python falls back to __len__ for truthiness: a length of zero makes the object falsy. If both are defined, __bool__ takes precedence.

class Inventory: def __init__(self, count): self.count = count def __len__(self): return self.count def __bool__(self): return self.count > 0

In this case, len(inv) and bool(inv) can disagree if count is negative, so keep the two methods consistent.

len() and Memory-Mapped or Lazy Containers

Some container-like objects, such as array.array, memoryview, and numpy.ndarray, implement __len__ for their first dimension. For a memoryview, len() returns the number of elements in the first dimension, not the total number of bytes.

import array arr = array.array("i", [1, 2, 3, 4]) print(len(arr)) # 4

For multi-dimensional arrays from third-party libraries, len() typically returns the size of the first axis. This is consistent with Python's sequence protocol, where len() corresponds to obj[0] being valid. If you need the total element count of a multi-dimensional array, use the library's own shape or size attribute rather than len().

This distinction becomes relevant when writing generic code that handles both flat sequences and nested containers. Relying on len() alone will not give you the total element count for nested structures.

matrix = [[1, 2], [3, 4]] print(len(matrix)) # 2, the number of rows

To count all elements, you would need to sum len(row) for each row, which is an O(n) operation over the rows.

python len function: Practical Usage and Code Examples | RYUSLOG DEV