Why do tuples take less space in memory than lists?
tuple takes less memory space in Python:
>>> a = (1,2,3) >>> a.__sizeof__() 48
lists takes more memory space:
>>> b = [1,2,3] >>> b.__sizeof__() 64
What happens internally on the Python memory management?
I assume you're using CPython and with 64bits (I got the same results on my CPython 2.7 64-bit). There could be differences in other Python implementations or if you have a 32bit Python.
Regardless of the implementation,
lists are variable-sized while
tuples are fixed-size.
tuples can store the elements directly inside the struct, lists on the other hand need a layer of indirection (it stores a pointer to the elements). This layer of indirection is a pointer, on 64bit systems that's 64bit, hence 8bytes.
But there's another thing that
lists do: They over-allocate. Otherwise
list.append would be an
O(n) operation always - to make it amortized
O(1) (much faster!!!) it over-allocates. But now it has to keep track of the allocated size and the filled size (
tuples only need to store one size, because allocated and filled size are always identical). That means each list has to store another "size" which on 64bit systems is a 64bit integer, again 8 bytes.
lists need at least 16 bytes more memory than
tuples. Why did I say "at least"? Because of the over-allocation. Over-allocation means it allocates more space than needed. However, the amount of over-allocation depends on "how" you create the list and the append/deletion history:
>>> l = [1,2,3] >>> l.__sizeof__() 64 >>> l.append(4) # triggers re-allocation (with over-allocation), because the original list is full >>> l.__sizeof__() 96 >>> l =  >>> l.__sizeof__() 40 >>> l.append(1) # re-allocation with over-allocation >>> l.__sizeof__() 72 >>> l.append(2) # no re-alloc >>> l.append(3) # no re-alloc >>> l.__sizeof__() 72 >>> l.append(4) # still has room, so no over-allocation needed (yet) >>> l.__sizeof__() 72
I decided to create some images to accompany the explanation above. Maybe these are helpful
This is how it (schematically) is stored in memory in your example. I highlighted the differences with red (free-hand) cycles:
That's actually just an approximation because
int objects are also Python objects and CPython even reuses small integers, so a probably more accurate representation (although not as readable) of the objects in memory would be:
tuplestruct in CPython repository for Python 2.7
liststruct in CPython repository for Python 2.7
intstruct in CPython repository for Python 2.7
__sizeof__ doesn't really return the "correct" size! It only returns the size of the stored values. However when you use
sys.getsizeof the result is different:
>>> import sys >>> l = [1,2,3] >>> t = (1, 2, 3) >>> sys.getsizeof(l) 88 >>> sys.getsizeof(t) 72
There are 24 "extra" bytes. These are real , that's the garbage collector overhead that isn't accounted for in the
__sizeof__ method. That's because you're generally not supposed to use magic methods directly - use the functions that know how to handle them, in this case:
sys.getsizeof (which actually adds the GC overhead to the value returned from