Thank you for fleshing things out!
I agree, but numbers emphatically do not “speak for themselves”. Spelling out “the point” explicitly is always better than leaving a reader to guess at what you think the numbers “mean”.
Yes and no
. Object memory is always larger under CPython, because its small object allocator aligns to a 16-byte boundary, twice as large as a pointer on 64-bit boxes. “Almost all objects” end up taking at least 32-bytes, so even almost all the smallest objects take 4x as much RAM as a pointer (this is true, e.g, even of tiny ints, which is as small as non-trivial objects get).
OTOH, lists don’t always hold pointers to distinct objects. Each pointer in a list takes up its own 8 bytes, but what they point at can be shared. For example, I have a bunch of apps that routinely build up multi-million-element lists of tiny ints. But tiny ints in CPython are singleton objects (implementation detail), so the storage for the ints is tiny and constant regardless of how large the lists get. It’s the space for the pointers that grows without bound.
Alas, there is no “typical app”, and Python wants to work well with all apps.
And I agree. It’s an old decision from a time when HW realities were much different. Best I can tell, there is no surviving contemporaneous discussion of the thinking that went into picking 1.125, so even if it doesn’t change, it would be good to create a public record now of why that remains the best choice.
There are also variations that could be considered. The OP is right that 1.5 and 2.0 are overwhelmingly most common now in other similar implantations. But, e.g., Go appears to use 2.0 for “small” vectors, but backs off to 1.25 for larger ones.
For a start, someone needs to change the shift count from 3 to 2 in listobject.c, and see whether it makes a measurable time difference on real-life programs they care about. To spell that out, that would change the growth factor from 1.125 to 1.25. If that pays off, go on to try 1.5 (shift count 1) and 2.0 (no shift).
There are “head arguments” for why 1.5 could do better than 2.0 (having to do with reducing system malloc heap fragmentation), but I don’t really trust such stuff. Modern boxes are so complex that nothing can substitute for careful timing of how they actually work. Counterintuitive results abound.