No history yet

Python Object Internals

The PyObject Foundation

Every object in CPython is a C struct. At the very bottom of this hierarchy sits PyObject, a small but crucial structure that contains everything the interpreter needs to manage an object. It's the common header for all other Python objects.

// A simplified view of the PyObject struct
typedef struct _object {
    Py_ssize_t ob_refcnt;  // Reference count
    struct _typeobject *ob_type; // Pointer to the type object
} PyObject;

The ob_refcnt field is the heart of Python's memory management. It's a counter that tracks how many references point to the object. When this count drops to zero, the memory for that object can be reclaimed. The ob_type is a pointer to another struct, PyTypeObject, which defines the object's type and behaviour. This struct is much larger and contains function pointers for operations like __add__ (addition) or __repr__ (representation). For variable-sized objects like lists or strings, the header is extended into a PyVarObject struct, which adds an ob_size field to track the number of items.

Memory Optimisation Tricks

Dynamic typing is flexible, but it comes with a memory cost. Every Python object carries the overhead of its PyObject header. To mitigate this, CPython employs several clever optimisation strategies, particularly for commonly used immutable types like small integers and short strings.

This process is called interning: storing only one copy of each distinct immutable value.

For integers, CPython pre-allocates a global array of PyLongObject structs for all integers between -5 and 256. When your code uses one of these numbers, Python doesn't create a new object. Instead, it just returns a pointer to the existing one. This is why a = 256; b = 256; a is b evaluates to True, but the same test with 257 typically evaluates to False. It's a significant memory and speed optimisation for loops and common calculations.

Lesson image

String interning works similarly. Python's compiler identifies strings that look like identifiers (e.g., function names, variable names, dictionary keys) and interns them. This ensures that only one copy of a given identifier string exists in memory, which speeds up dictionary lookups by allowing pointer comparisons instead of character-by-character string comparisons. Since was implemented, string representation in memory is also flexible, using 1, 2, or 4 bytes per character depending on the string's content. This avoids the waste of using UTF-32 for purely ASCII text.

Controlling Object Layout

By default, every custom object you create in Python gets a __dict__ attribute. This is a dictionary that stores the object's instance attributes. It's incredibly flexible, allowing you to add or remove attributes at runtime. However, dictionaries themselves have significant memory overhead.

When you need to create millions of instances of a class with a fixed set of attributes, this overhead becomes a problem. The solution is __slots__. By defining __slots__ in a class, you tell Python not to use a __dict__. Instead, it allocates space for the specified attributes directly within the object's memory layout, similar to a C struct.

class Point:
    # This tells Python to reserve space for exactly 'x' and 'y',
    # and to forgo creating a __dict__ for each instance.
    __slots__ = ['x', 'y']

    def __init__(self, x, y):
        self.x = x
        self.y = y

# p = Point(1, 2)
# p.z = 3  # This would raise an AttributeError

The result is a significantly smaller memory footprint per object and slightly faster attribute access. The trade-off is the loss of flexibility. You can no longer add new attributes to instances on the fly. This makes __slots__ an essential optimisation tool for performance-critical applications dealing with large numbers of objects.

Understanding these internal mechanisms is key to writing high-performance Python code. It allows you to make informed decisions about data structures and object design, moving beyond the language's surface-level abstractions to control its runtime behaviour directly.