skip to content

What is the difference between len(a_list) and sys.getsizeof(a_list) in Python?

level: juniorimportance: should knowfreq 35%

answer

  1. One counts, the other measures
  2. Container bytes versus contents
  3. Spare slots are counted too
  4. Pointers, not the objects behind them
  5. sys.getsizeof is shallow

basics

~20 s

len returns how many elements a list currently holds. sys.getsizeof returns the bytes the list object itself occupies: its header plus its internal array of pointer slots, including spare unused ones. It never counts the objects the list points at.

solid answer

~50 s

`len(lst)` reads the element count stored in the list itself, so it is a constant-time field read. `sys.getsizeof(lst)` asks how many bytes *that object* occupies: a small header plus, on a 64-bit build, eight bytes for every slot CPython has **allocated** for it. That is usually more than `len` suggests, because a list keeps spare slots so `append` rarely has to resize — which is why the reported size climbs in a staircase and can drop again when the list shrinks or is cleared. Crucially it is a shallow measurement: a list is an array of pointers, so a list of ten references to a 10 MB string reports the same size as a list of ten small integers. For a real footprint you have to sum the elements yourself, de-duplicating shared objects, or use `tracemalloc`.

code

python · 11 lines
python
import sys

lst = []
last = sys.getsizeof(lst)
print(f"len={len(lst):3d}  getsizeof={last}")
for i in range(100):
    lst.append(i)
    size = sys.getsizeof(lst)
    if size != last:
        print(f"len={len(lst):3d}  getsizeof={size}")
        last = size

go deeper

for a junior

Be ready to say in one sentence that len counts items while sys.getsizeof measures the list object's own bytes, and that the second one does not include the objects stored in the list.

for a middle

Explain the mechanics: the list is a header plus an array of pointer slots, getsizeof counts allocated slots including spares, which is why the number moves in steps as you append and can fall when the list shrinks.

for a senior

Show you know when the number is useless. Demonstrate a deep-sizing walk with identity de-duplication, and say you would reach for tracemalloc and resident set size rather than getsizeof for any real capacity question.

for a principal

Own the guidance your team follows: getsizeof values are build-specific implementation details, so treat any memory budget derived from them as an observation, not a contract, and standardize on allocation tracing for sizing decisions.

### Two questions that look like one `len(lst)` and `sys.getsizeof(lst)` get typed one after the other in a REPL often enough that they start to look like two views of the same fact. They are not. `len` asks *how many items are in this container*. `sys.getsizeof` asks *how many bytes does this object itself occupy*. On a list the two numbers are related only loosely, and every part of that relationship is a CPython implementation detail rather than a language guarantee. ### What `len` reads For a built-in list, `len` reads the element count stored directly in the list's own structure, so it is a constant-time field read — it never walks the list. For a user-defined class `len` calls that class's `__len__`, which is ordinary Python code and can be as slow as its author made it (and must return a non-negative integer, or `len` raises `TypeError` or `OverflowError`). Either way the answer is a count of items, not a measure of memory. ### What a CPython list actually is A list object is a small fixed-size header — reference count, type pointer, element count — plus two more fields: a pointer to a *separately allocated* array, and the number of slots that array has room for (its capacity). The array holds object pointers, eight bytes each on a 64-bit build. The objects those pointers reach live elsewhere on the heap and are shared freely with any other name or container that refers to them. ### What `sys.getsizeof` reports `sys.getsizeof` calls the object's `__sizeof__` and, for a container that the cyclic garbage collector tracks, adds the bytes of the GC bookkeeping header. For a list that comes to: the header plus GC overhead (a few dozen bytes on a 64-bit build) plus eight bytes for every **allocated** slot. The word *allocated* is the interesting one — not *used*. A list keeps spare slots so that `append` usually does not have to reallocate, and `sys.getsizeof` counts those spares. That is why the reported size climbs in a staircase rather than one slot at a time: it jumps when the list outgrows its capacity and stays flat while spare slots are being filled. It can also go *down* — when a list shrinks well below its capacity CPython reallocates a smaller array — and `clear()` or `del lst[:]` releases the array entirely, dropping the report back to the bare header. ### Shallow, always shallow The measurement stops at the pointer array. A list holding ten references to a 10 MB string reports the same size as a list holding ten small integers, because in both cases it stores ten pointers. This is not a defect; it is the only answer that composes, since the same object can be referenced from many containers and no container can claim to *own* it. Getting a real footprint therefore means walking the elements yourself and summing their sizes — and immediately meeting the traps that make the walk approximate: the same object may appear many times and must be de-duplicated by identity, short strings and small integers are shared interpreter-wide so charging them to your structure overstates it, the allocator rounds each small object up to a size class, and a container's `__sizeof__` reports only that object's own storage, so nested containers have to be recursed into by hand. ### When to reach for which Use `len` for anything about contents — iteration bounds, emptiness, capacity policies in your own code. Use `sys.getsizeof` to reason about *one object's own overhead*: comparing a list against a tuple of the same length, seeing how much slack over-allocation leaves, or checking what a class costs per instance. Do not use it for capacity planning: for "how much RAM will this service need" the honest instruments are `tracemalloc` snapshots (which attribute allocations to the code that made them) and the process's resident set size. Finally, the exact numbers are build-specific. A 32-bit build has four-byte pointers. The free-threaded build — officially supported as of Python 3.14 — carries different per-object bookkeeping. Nothing in the language specification promises any particular value, so treat every number you print as an observation about the interpreter in front of you. ### What the interviewer is listening for Two sentences carry the answer: *a list stores pointers, not objects*, and *`sys.getsizeof` measures the container, not the contents*. A candidate who says that, then adds that the size counts over-allocated spare slots so it does not track `len` one-for-one, has demonstrated they know what a Python list is made of — which is the real subject of the question.

  • How would you find the real memory footprint of a list of strings?
    Sum `sys.getsizeof` over the elements as well as the list, de-duplicating by `id` so a shared object is charged once, and recursing into nested containers. That is still approximate: short strings and small integers are shared interpreter-wide, and the allocator rounds each object up to a size class. For a service, `tracemalloc` snapshots and process resident set size are the honest instruments.
  • Does len() ever run Python code?
    For a built-in list, no — it reads a count field, so it is O(1). For a user-defined class it calls that class's `__len__`, which is ordinary Python and can be arbitrarily slow or can raise. `__len__` must return a non-negative integer; returning something else makes `len` raise `TypeError`, and returning a value too large for a machine word raises `OverflowError`.

len is the number of parking spaces occupied; sys.getsizeof is the size of the car park, empty bays included. Neither tells you how heavy the cars are.

saying these in an interview costs you the question

  • Says sys.getsizeof returns the total memory of a list and its contents
  • Thinks getsizeof is always len times eight plus a fixed header
  • Expects getsizeof to grow by one slot on every append
  • Believes a list stores its elements inline rather than as pointers
  • Claims clearing a list cannot change its reported size
  • Treats getsizeof numbers as guaranteed by the language

context