Cost of len function
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Python's built-in len() function runs in O(1) constant time for all built-in container types (list, tuple, dict, set, str, bytes). It does not iterate over elements — it reads a pre-stored length field from the object's C struct. This makes len() effectively free and safe to call in loops, conditionals, and hot paths without performance concerns.
How len() Works Internally
Every built-in Python container stores its size as an integer field in the underlying C struct. When you call len(obj), CPython calls obj.__len__(), which reads this field directly.
The CPython source for list.__len__ is essentially:
Comparison with Other Languages
| Language | Operation | Time Complexity |
| Python | len(x) | O(1) for all built-in types |
| Java | list.size(), map.size() | O(1) |
| Java | array.length | O(1) — field access |
| C | strlen(s) | O(n) — scans for null terminator |
| Go | len(x) | O(1) for slices, maps, strings, channels |
| JavaScript | arr.length | O(1) — property access |
| Rust | vec.len() | O(1) — stored field |
The notable exception is C's strlen(), which must scan the entire string to find the null terminator. Python's len() on strings does not have this cost.
Custom len Method
You can define __len__ on your own classes to support len():
The cost of len() on custom objects depends entirely on your __len__ implementation. If it iterates, it becomes O(n):
Using len() in Loops and Conditionals
Since len() is O(1), these patterns are all efficient:
There is no performance benefit to caching len() in a variable for built-in types:
len() vs Truthiness for Empty Checks
Both approaches work for checking emptiness, but the Pythonic convention is to use truthiness:
Both are O(1). The truthiness check calls __bool__ first; if not defined, it falls back to __len__ and checks if the result is nonzero.
Generators and Iterators Have No len()
Common Pitfalls
- Assuming len() is O(n) and caching it unnecessarily:
len()is O(1) for all built-in types. Writingn = len(lst)before a loop adds a variable for no benefit. The only reason to cache is if the list changes during the loop and you need the original length. - Calling len() on a generator: Generators do not have a length because they produce values lazily. Calling
len()raisesTypeError. Convert to a list first withlist(gen)or usesum(1 for _ in gen)(but this consumes the generator). - Implementing slow len on custom classes: If your
__len__iterates over data,len(obj)becomes O(n). This breaks the expectation thatlen()is cheap. Maintain a counter that updates on add/remove instead. - Using len() to check for None:
len(x) > 0raisesTypeErrorifxisNone. Checkif x is not None and len(x) > 0or useif xwhich handlesNonegracefully (sinceNoneis falsy). - Confusing len() with sys.getsizeof():
len()returns the number of elements.sys.getsizeof()returns the memory size in bytes. They measure different things — a list with 1000 small integers haslen()of 1000 butgetsizeof()around 8056 bytes.
Summary
len()is O(1) for all built-in Python types — it reads a stored integer, never iterates- CPython containers (list, dict, set, str, tuple, bytes) store their size in the underlying C struct
- No need to cache
len()in a variable for performance — call it freely - Custom
__len__methods determine the cost for user-defined classes — keep them O(1) - Generators and iterators do not support
len()— usesum(1 for _ in gen)to count - Use truthiness (
if my_list:) rather thanlen(my_list) > 0for idiomatic empty checks

