len(obj) simply calls obj.__len__():

>>> [1, 2, 3, 4].__len__()
4

It is therefore not correct to say that len() is always O(1) -- calling len() on most objects (e.g. lists) is O(1), but an arbitrary object might implement __len__ in an arbitrarily inefficient way.

max(obj) is a different story, because it doesn't call a single magic __max__ method on obj; it instead iterates over it, calling __iter__ and then calling __next__. It does this n times (and also does a comparison each time to track the max item it's seen so far), so it must always be at least O(n). (It can be slower if __next__ or the comparison methods are slow, although that would be very unusual.)

For either of these, we don't count the time it took to build the collection as part of the cost of calling the operation itself -- this is because you might build a list once and then call len() on it many times, and it's useful to know that the len() by itself is very cheap even if building the list was very expensive.

Answer from Samwise on Stack Overflow
Top answer
1 of 3
8

len(obj) simply calls obj.__len__():

>>> [1, 2, 3, 4].__len__()
4

It is therefore not correct to say that len() is always O(1) -- calling len() on most objects (e.g. lists) is O(1), but an arbitrary object might implement __len__ in an arbitrarily inefficient way.

max(obj) is a different story, because it doesn't call a single magic __max__ method on obj; it instead iterates over it, calling __iter__ and then calling __next__. It does this n times (and also does a comparison each time to track the max item it's seen so far), so it must always be at least O(n). (It can be slower if __next__ or the comparison methods are slow, although that would be very unusual.)

For either of these, we don't count the time it took to build the collection as part of the cost of calling the operation itself -- this is because you might build a list once and then call len() on it many times, and it's useful to know that the len() by itself is very cheap even if building the list was very expensive.

2 of 3
6

Let's check it:

import time
from matplotlib import pyplot as plt
import numpy as np


def main():
    a = []
    data = []
    for i in range(10_000):
        a.append(i)
        ts_len = time.time()
        _ = len(a)
        te_len = time.time()

        ts_max = time.time()
        _ = max(a)
        te_max = time.time()

        ts_min = time.time()
        _ = min(a)
        te_min = time.time()

        data.append([i, te_len - ts_len, te_max - ts_max, te_min - ts_min])

    data = np.array(data)
    plt.plot(data[:, 0], data[:, 1], "-r", label="len")
    plt.plot(data[:, 0], data[:, 2], "--g", label="max")
    plt.plot(data[:, 0], data[:, 2], ".b", label="min")
    plt.title("Len/max/min")
    plt.xlabel("Size of the list")
    plt.ylabel("Time elapsed (s)")
    plt.legend()
    plt.show()


if __name__ == '__main__':
    main()

Discussions

python - Complexity of len() with regard to sets and lists - Stack Overflow
Releases Keep up-to-date on features we add to Stack Overflow and Stack Internal. ... Find centralized, trusted content and collaborate around the technologies you use most. Learn more about Collectives ... Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... The complexity of len() with regards to sets and lists is equally O(1). How come it takes more time to process sets? ~$ python ... More on stackoverflow.com
🌐 stackoverflow.com
c - What is the secret behind Python's len() builtin time complexity of O(1) - Stack Overflow
Since Python is implemented in C, I am confused how the developers managed to make the Python builtin len function run on any sequence in constant time, O(1), while C's string function strlen runs in More on stackoverflow.com
🌐 stackoverflow.com
September 2, 2018
python - Time complexity of accessing collections.deque length - Stack Overflow
Releases Keep up-to-date on features we add to Stack Overflow and Stack Internal. ... Find centralized, trusted content and collaborate around the technologies you use most. Learn more about Collectives ... Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... What is the time complexity of calling Python's built-in len... More on stackoverflow.com
🌐 stackoverflow.com
Time complexity for running len(array) in python? - Stack Overflow
How does Python find the length of the array? Is it stored somewhere in an internal data-structure or does it have to iterate through the entire thing to find the length? I ask because I'm using i... More on stackoverflow.com
🌐 stackoverflow.com
June 12, 2017
Top answer
1 of 7
125

Firstly, you have not measured the speed of len(), you have measured the speed of creating a list/set together with the speed of len().

Use the --setup argument of timeit:

$ python -m timeit --setup "a=[1,2,3,4,5,6,7,8,9,10]" "len(a)"
10000000 loops, best of 3: 0.0369 usec per loop
$ python -m timeit --setup "a={1,2,3,4,5,6,7,8,9,10}" "len(a)"
10000000 loops, best of 3: 0.0372 usec per loop

The statements you pass to --setup are run before measuring the speed of len().

Secondly, you should note that len(a) is a pretty quick statement. The process of measuring its speed may be subject to "noise". Consider that the code executed (and measured) by timeit is equivalent to the following:

for i in itertools.repeat(None, number):
    len(a)

Because both len(a) and itertools.repeat(...).__next__() are fast operations and their speeds may be similar, the speed of itertools.repeat(...).__next__() may influence the timings.

For this reason, you'd better measure len(a); len(a); ...; len(a) (repeated 100 times or so) so that the body of the for loop takes a considerably higher amount of time than the iterator:

$ python -m timeit --setup "a=[1,2,3,4,5,6,7,8,9,10]" "$(for i in {0..1000}; do echo "len(a)"; done)"
10000 loops, best of 3: 29.2 usec per loop
$ python -m timeit --setup "a={1,2,3,4,5,6,7,8,9,10}" "$(for i in {0..1000}; do echo "len(a)"; done)"
10000 loops, best of 3: 29.3 usec per loop

(The results still says that len() has the same performances on lists and sets, but now you are sure that the result is correct.)

Thirdly, it's true that "complexity" and "speed" are related, but I believe you are making some confusion. The fact that len() has O(1) complexity for lists and sets does not imply that it must run with the same speed on lists and sets.

It means that, on average, no matter how long the list a is, len(a) performs the same asymptotic number of steps. And no matter how long the set b is, len(b) performs the same asymptotic number of steps. But the algorithm for computing the size of lists and sets may be different, resulting in different performances (timeit shows that this is not the case, however this may be a possibility).

Lastly,

If the creation of a set object takes more time compared to creating a list, what would be the underlying reason?

A set, as you know, does not allow repeated elements. Sets in CPython are implemented as hash tables (to ensure average O(1) insertion and lookup): constructing and maintaining a hash table is much more complex than adding elements to a list.

Specifically, when constructing a set, you have to compute hashes, build the hash table, look it up to avoid inserting duplicated events and so on. By contrast, lists in CPython are implemented as a simple array of pointers that is malloc()ed and realloc()ed as required.

2 of 7
21

The relevant lines are http://svn.python.org/view/python/trunk/Objects/setobject.c?view=markup#l640

640     static Py_ssize_t
641     set_len(PyObject *so)
642     {
643         return ((PySetObject *)so)->used;
644     }

and http://svn.python.org/view/python/trunk/Objects/listobject.c?view=markup#l431

431     static Py_ssize_t
432     list_length(PyListObject *a)
433     {
434         return Py_SIZE(a);
435     }

Both are only a dynamic lookup.

So what is the difference you may ask. You measure the creation of the objects, too. And it is a little more time consuming to create a set than a list.

🌐
Python
wiki.python.org › moin › TimeComplexity
TimeComplexity - Python Wiki
As seen in the source code the complexities for set difference s-t or s.difference(t) (set_difference()) and in-place set difference s.difference_update(t) (set_difference_update_internal()) are different! The first one is O(len(s)) (for every element in s add it to the new set, if not in t).
🌐
Medium
medium.com › @hamzaehsankhan › why-len-list-has-a-time-complexity-of-o-1-a-dive-into-cpython-cbed75c38b54
Why len(list) has a time-complexity of O(1)? — A dive into CPython | by Hamza Ehsan Khan | Medium
July 13, 2023 - This in-built function is not at the mercy of the size of the list. Whether your list contains 1 element or 1000, as per the default implementation of Python (CPython), the time-complexity is O(1).
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › python › complexity-of-len-with-regard-to-sets-and-lists
Complexity Of Len() With Regard To Sets And Lists - GeeksforGeeks
July 23, 2025 - The length attribute is maintained and updated in constant time as elements are added or removed. ... The space complexity of the len() operation on lists is also constant. It doesn't require additional space proportional to the size of the list. The length attribute is a fixed-size attribute associated with the list object. Sets in Python are implemented as hash tables.
🌐
Reddit
reddit.com › r/learnpython › why is the complexity of len(list) o(1)?
r/learnpython on Reddit: Why is the complexity of len(list) O(1)?
October 8, 2017 -

I was looking at the time complexity of different list operations and the "get length" operation has constant time O(1). Why is that? I would understand it if lists could only store a single type of object (e.g. only integers or characters), but since lists can hold objects of a different size I don't understand how the len() function has O(1) complexity.

🌐
Reddit
reddit.com › r/leetcode › why len(list) has a time-complexity of o(1)?
r/leetcode on Reddit: Why len(list) has a time-complexity of O(1)?
July 13, 2023 - Any time you allocate an array on the heap and not the stack, you’ll only have a pointer to it, so you sizeof will once again return 4 or 8 depending on the OS. i.e. just use a vector of some other implementation that actually keeps track of its size. ... a more interesting question surrounds insertion and removal on the list in python. how can they claim theta(1) speed? ... Caveat: only theta(1) on end insertion/removal and only amortized not true complexity.
Top answer
1 of 1
13

Do you not have to iterate through the whole array to count how many indices its taking up?

No, you do not.

You can generally always trade space for time when constructing algorithms.

For example, when creating a collection, allocate a separate variable holding the size. Then increment that when adding an item to the collection and decrement it when removing something.

Then, voilà, the size of the collection can then be obtained in O(1) time just by accessing that variable.

And this appears to be what Python actually does, as per this page, which states (checking of the Python source code shows that this is the action when requesting the size of a great many objects):

Py_SIZE(o) - This macro is used to access the ob_size member of a Python object. It expands to (((PyVarObject*)(o))->ob_size).


If you compare the two approaches (iterating vs. a length variable), the properties of each can be seen in the following table:

Measurement Iterate Variable
Space needed No extra space beyond the collection itself. Tiny additional length (4 bytes allowing for size of about four billion).
Time taken Iteration over the collection.
Depends on collection size, so could be significant.
Extraction of length, very quick.
Changes to list size (addition or deletion) incur slight extra expense of updating length, but this is also tiny.

In this case, the extra cost is minimal but the time saved for getting the length could be considerable, so it's probably worth it.

That's not always the case since, in some (rare) situations, the added space cost may outweigh the reduced time taken (or it may need more space than can be made available).


And, by way of example, this is what I'm talking about. Ignore the fact that it's totally unnecessary in Python, this is for a mythical Python-like language that has an O(n) cost for finding the length of a list:

import random

class FastQueue:
    """ FastQueue: demonstration of length variable usage.
    """
    def __init__(self):
        """ Init: Empty list and set length zero.
        """
        self._content = []
        self._length = 0

    def push(self, item):
        """ Push: Add to end, increase length.
        """
        self._content.append(item)
        self._length += 1

    def pull(self):
        """ Pull: Remove from front, decrease length, taking
                  care to handle empty queue.
        """
        item = None
        if self._length > 0:
            item = self._content[0]
            self._content = self._content[1:]
            self._length -= 1
        return item

    def length(self):
        """ Length: Just return stored length. Obviously this
                    has no advantage in Python since that's
                    how it already does length. This is just
                    an illustration of my answer.
        """
        return self._length

    def slow_length(self):
        """ Length: A slower version for comparison.
        """
        list_len = 0
        for _ in self._content:
            list_len += 1
        return list_len

""" Test harness to ensure I haven't released buggy code :-)
"""

queue = FastQueue()

for _ in range(10):
    val = random.randint(1, 50)
    queue.push(val)
    print(f'push {val}, length = {queue.length()}')

for _ in range(11):
    print(f'pull {queue.pull()}, length = {queue.length()}')
🌐
Data Science Parichay
datascienceparichay.com › home › blog › get length of a list in python
Get Length of a List in Python - Data Science Parichay
May 12, 2021 - The len() function is O(1) in time complexity whereas using a loop is O(n). This means that the len() function is very quick in determining the length of a list. It takes constant time and does not depend on the size of the list itself.
Top answer
1 of 1
1

In C strlen() is an O(n) operation because it has to scan for the \0 terminator. Very few languages use C's representation for strings. Most, including Python, store the length in a separate field so it doesn't have to be computed on demand.

See PEP 393 for an exact specification of how Python stores strings. It's complicated because it switches between representations to save space when strings consist solely of ASCII characters, but the field in question is the first one, length.

typedef struct {
  PyObject_HEAD
  Py_ssize_t length;                    // <--------------
  Py_hash_t hash;
  struct {
      unsigned int interned:2;
      unsigned int kind:2;
      unsigned int compact:1;
      unsigned int ascii:1;
      unsigned int ready:1;
  } state;
  wchar_t *wstr;
} PyASCIIObject;

typedef struct {
  PyASCIIObject _base;
  Py_ssize_t utf8_length;
  char *utf8;
  Py_ssize_t wstr_length;
} PyCompactUnicodeObject;

typedef struct {
  PyCompactUnicodeObject _base;
  union {
      void *any;
      Py_UCS1 *latin1;
      Py_UCS2 *ucs2;
      Py_UCS4 *ucs4;
  } data;
} PyUnicodeObject;

The fields have the following interpretations:

  • length: number of code points in the string (result of sq_length)
  • interned: interned-state (SSTATE_*) as in 3.2
  • kind: form of string
    • 00 => str is not initialized (data are in wstr)
    • 01 => 1 byte (Latin-1)
    • 10 => 2 byte (UCS-2)
    • 11 => 4 byte (UCS-4);
  • compact: the object uses one of the compact representations (implies ready)
  • ascii: the object uses the PyASCIIObject representation (implies compact and ready)
  • ready: the canonical representation is ready to be accessed through PyUnicode_DATA and PyUnicode_GET_LENGTH. This is set either if the object is compact, or the data pointer and length have been initialized.
  • wstr_length, wstr: representation in platform's wchar_t (null-terminated). If wchar_t is 16-bit, this form may use surrogate pairs (in which cast wstr_length differs form length). wstr_length differs from length only if there are surrogate pairs in the representation.
  • utf8_length, utf8: UTF-8 representation (null-terminated).
  • data: shortest-form representation of the unicode string. The string is null-terminated (in its respective representation).

Full source code for the CPython string implementation is on GitHub.

🌐
Finxter
blog.finxter.com › home › learn python blog › python list length – what’s the runtime complexity of len()?
Python List Length - What's the Runtime Complexity of len()? - Be on the Right Side of Change
April 8, 2020 - The runtime complexity of the len() function on your Python list is O(1). It takes constant runtime no matter how many elements are in the list. Why? Because the list object maintains an integer counter that increases and decreases as you add ...
🌐
GeeksforGeeks
geeksforgeeks.org › internal-working-of-the-len-function-in-python
Internal Working of the len() Function in Python - GeeksforGeeks
July 2, 2020 - Thus when you call the len() function, you do not give the interpreter the command to find the length by traversing, but rather you ask the interpreter to print a value that is already stored. Hence, len() function in Python runs in O(1) complexity.