Phase 1 of 25 · Topic 1.3

PyObject C-Struct & Dynamic Typing Overhead

1Concept

Every Python object in CPython is wrapped in a C `PyObject` structure containing `ob_refcnt` (reference counter) and `ob_type` (type descriptor pointer). An integer `42` in Python occupies 28 bytes in RAM compared to 4 bytes in C.

2Architecture Diagram

+-------------------------------------------------------------+
| CPython PyObject Structure Header (28 Bytes total for int) |
|  +---------------------------+---------------------------+  |
|  | ob_refcnt (8 Bytes)      | ob_type (*PyTypeObject)   |  |
|  +---------------------------+---------------------------+  |
|  | ob_digit / Value payload (12 Bytes)                   |  |
|  +-------------------------------------------------------+  |
+-------------------------------------------------------------+

3Code Example

Python 3.12
import sys

x = 42
text = "Hello Enterprise Python"
lst = [1, 2, 3]

print("=== Memory Allocation (sys.getsizeof) ===")
print(f"int (42): {sys.getsizeof(x)} bytes")
print(f"str ('{text}'): {sys.getsizeof(text)} bytes")
print(f"list ([1,2,3]): {sys.getsizeof(lst)} bytes")

4Expected Output

=== Memory Allocation (sys.getsizeof) ===
int (42): 28 bytes
str ('Hello Enterprise Python'): 70 bytes
list ([1,2,3]): 88 bytes

5Key Takeaways

  • All Python objects carry an 8-byte reference count and an 8-byte type pointer.
  • Dynamic typing requires pointer indirection for every method or attribute call.
  • Use NumPy arrays or __slots__ to eliminate PyObject overhead when processing millions of items.