Question presented to candidate:
"A Python list can grow forever with append(), and yet append() is called O(1). What is a list in memory, what happens when it runs out of room, and which list operations are actually slow?"
What a strong answer should cover:
- A list is a dynamic array: a small header (
ob_size= length,allocated= capacity) plus one contiguous array of pointers to the item objects, not the objects themselves. - Over-allocation: when
append()runs out of slots, CPython reallocates with extra room (about 12.5% plus a small constant, rounded to a multiple of 4), so resizes are rare andappend()is amortized O(1). - The observed growth sequence on CPython 3.11: capacities 4, 8, 16, 24, 32, 40, 52, 64, 76, 92, ... and shrinking only when fewer than half the slots are used.
- The costs: index and
append()/pop()at the end are O(1);insert(0, x),pop(0)anddel a[0]shift every pointer, O(n); usecollections.dequefor a queue. sys.getsizeof()counts only the header and pointer array, never the items; how a list is built ([0] * n,list(range(n)), a comprehension) changes its spare capacity.
Clarifying questions expected:
- Do you mean CPython specifically? (The growth rule is an implementation detail, not part of the language.)
Code / implementation expected: Optional β a sys.getsizeof() experiment that reveals the capacity steps, or an amortized-cost argument.