Skip to main content

Include/cpython/object.h

Include/cpython/object.h is the internal counterpart to the stable Include/object.h. It exposes the full layout of PyTypeObject (50-plus slots), the complete set of Py_TPFLAGS_* bitmasks, the foundational PyObject_HEAD / PyObject_VAR_HEAD macros, and PyHeapTypeObject for heap-allocated types that carry __dict__ and __weakref__. Code that only targets the stable ABI should include object.h; code that introspects or builds type objects directly needs this file.

Map

LinesSymbolRole
1–25PyObject_HEAD / PyObject_VAR_HEADStruct-prefix macros shared by every object layout
26–50PyObject_INIT / PyObject_INIT_VARInline initializers used by allocators
51–80_PyObject_SIZE / _PyObject_VAR_SIZECompile-time size helpers
81–180PyTypeObjectFull 50-plus slot struct definition
181–230Py_TPFLAGS_*Type-flag bitmask constants
231–270PyHeapTypeObjectHeap-type wrapper with ht_name, ht_slots, ht_module
271–300miscellaneous internal macrosPy_TYPE, Py_SIZE, Py_SET_TYPE, ref-count helpers

Reading

PyObject_HEAD and PyObject_VAR_HEAD

Every CPython object begins with PyObject_HEAD, which expands to a single PyObject ob_base member. Variable-length objects add an ob_size field via PyObject_VAR_HEAD.

// CPython: Include/cpython/object.h:7 PyObject_HEAD
#define PyObject_HEAD PyObject ob_base;

// CPython: Include/cpython/object.h:18 PyObject_VAR_HEAD
#define PyObject_VAR_HEAD PyVarObject ob_base;

The struct layouts for PyObject and PyVarObject live in the stable header. These macros just give C structs a named first member so that any pointer to a CPython object can be safely cast to PyObject *.

PyTypeObject: the full slot struct

PyTypeObject is the largest struct in CPython. The abbreviated excerpt below shows the major slot groups; each group corresponds to one of the sub-structs (PyNumberMethods, PySequenceMethods, PyMappingMethods, etc.) that the abstract protocol layer reads.

// CPython: Include/cpython/object.h:81 PyTypeObject
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize;
Py_ssize_t tp_itemsize;

destructor tp_dealloc;
Py_ssize_t tp_vectorcall_offset;
getattrfunc tp_getattr;
setattrfunc tp_setattr;

PyAsyncMethods *tp_as_async;
reprfunc tp_repr;

PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;

hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;

PyBufferProcs *tp_as_buffer;
unsigned long tp_flags;
const char *tp_doc;

traverseproc tp_traverse;
inquiry tp_clear;
richcmpfunc tp_richcompare;
/* ... 30+ further slots ... */
} PyTypeObject;

Py_TPFLAGS bitmasks

The tp_flags field packs many boolean properties into a single unsigned long. Key flags relevant to gopy:

// CPython: Include/cpython/object.h:181 Py_TPFLAGS_HEAPTYPE
#define Py_TPFLAGS_HEAPTYPE (1UL << 9)
#define Py_TPFLAGS_BASETYPE (1UL << 10)
#define Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
#define Py_TPFLAGS_READY (1UL << 12)
#define Py_TPFLAGS_HAVE_GC (1UL << 14)
#define Py_TPFLAGS_METHOD_DESCRIPTOR (1UL << 17)
#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 18)
#define Py_TPFLAGS_IMMUTABLETYPE (1UL << 19)

Py_TPFLAGS_HEAPTYPE distinguishes dynamically created types (classes defined with type() or the class statement) from static types compiled into the interpreter. Only heap types may have their tp_dict mutated after creation.

PyHeapTypeObject

Heap types carry extra fields beyond PyTypeObject for the name string, slot descriptors, and the module reference introduced in 3.10.

// CPython: Include/cpython/object.h:231 PyHeapTypeObject
typedef struct _heaptypeobject {
PyTypeObject ht_type; /* must be first */
PyAsyncMethods as_async;
PyNumberMethods as_number;
PyMappingMethods as_mapping;
PySequenceMethods as_sequence;
PyBufferProcs as_buffer;
PyObject *ht_name; /* str: class name */
PyObject *ht_slots; /* tuple of slot names or NULL */
PyObject *ht_qualname;
struct _Py_Identifier *ht_id_name;
PyObject *ht_module; /* __module__ for 3.10+ */
char *ht_tpname; /* tp_name points here */
} PyHeapTypeObject;

Slots for number, sequence, mapping, and buffer methods are embedded inline so that the pointer members of PyTypeObject can point directly into the same heap allocation, avoiding a second indirection.

gopy notes

PyTypeObject is mirrored in objects/type.go as the Type struct. Not every CPython slot has a Go counterpart yet. The current mapping:

  • tp_call maps to the Callable Go interface on Type.
  • tp_richcompare is a field RichCompare func(a, b Object, op int) (Object, error).
  • tp_as_number, tp_as_sequence, tp_as_mapping are represented as embedded Go interface fields (NumberProtocol, SequenceProtocol, MappingProtocol) in objects/protocol.go.

Py_TPFLAGS_* constants are redeclared in objects/type.go as Go const block values. Py_TPFLAGS_HEAPTYPE and Py_TPFLAGS_IMMUTABLETYPE are used by objects/usertype.go when building class objects from the class statement.

PyHeapTypeObject corresponds to objects/usertype.go's UserType struct, which embeds Type and adds HtName, HtSlots, HtQualname, and HtModule.

CPython 3.14 Changes

  • Py_TPFLAGS_MANAGED_DICT (bit 4) was added in 3.11 and is now relied upon by the per-object dict optimization; Include/cpython/object.h documents it prominently in 3.14.
  • PyHeapTypeObject.ht_tpname was added in 3.12 to store the UTF-8 name buffer inline, removing the need for a separate PyMem_Malloc call during type creation. 3.14 documentation clarifies its lifetime rules.
  • The tp_vectorcall_offset field interpretation is now formally stable in 3.14, having been an internal implementation detail in earlier releases.