Skip to content

Instantly share code, notes, and snippets.

@RezSat
Created June 18, 2025 13:17
Show Gist options
  • Save RezSat/e23d767605c1caa44f48ac4db7548439 to your computer and use it in GitHub Desktop.
Save RezSat/e23d767605c1caa44f48ac4db7548439 to your computer and use it in GitHub Desktop.
A Minimal Custom Type in C To illustrate the `PyTypeObject` in action, the following C code outlines a minimal custom type, `SimpleObject`. This type exposes a single integer member and provides a basic `__repr__` method.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h> // For PySys_WriteStdout
// 1. Define the instance structure (must start with PyObject_HEAD)
typedef struct {
PyObject_HEAD
long value; // Our custom data member
} SimpleObject;
// 2. Implement tp_dealloc: Frees the object's memory
static void
SimpleObject_dealloc(SimpleObject *self) {
Py_TYPE(self)->tp_free((PyObject *)self); // Call the type's free function
}
// 3. Implement tp_repr: Provides string representation (__repr__)
static PyObject *
SimpleObject_repr(SimpleObject *self) {
return PyUnicode_FromFormat("<SimpleObject value=%ld at %p>",
self->value, (void *)self);
}
// 4. Implement tp_new: Creates new instances (__new__)
static PyObject *
SimpleObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
SimpleObject *self;
long initial_value = 0; // Default value
// Parse optional initial value
if (!PyArg_ParseTuple(args, "|l", &initial_value)) {
return NULL;
}
// Allocate memory for the object
self = (SimpleObject *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
// Initialize our custom member
self->value = initial_value;
return (PyObject *)self;
}
// 5. Define the PyTypeObject structure
static PyTypeObject SimpleType = {
PyVarObject_HEAD_INIT(NULL, 0) // Initializes ob_refcnt and ob_type (set by PyType_Ready)
.tp_name = "simple_module.SimpleObject", // Type name
.tp_basicsize = sizeof(SimpleObject), // Size of instance
.tp_dealloc = (destructor)SimpleObject_dealloc, // Deallocation function
.tp_repr = (reprfunc)SimpleObject_repr, // Representation function
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // Default flags, allow subclassing
.tp_doc = "A simple custom object with an integer value.", // Docstring
.tp_new = SimpleObject_new, // Object creation function
};
// ---
// Corrected Module method definitions
// Declare SimpleModuleMethods as an ARRAY of PyMethodDef
static PyMethodDef SimpleModuleMethods[] = {
{NULL, NULL, 0, NULL} // Sentinel
};
// ---
// 7. Define module structure
static struct PyModuleDef simplemodule = {
PyModuleDef_HEAD_INIT,
"simple_module", // Module name
"A module defining a simple custom object.", // Module docstring
-1, // Size of per-interpreter state
SimpleModuleMethods // Module methods (now correctly a pointer to the array)
};
// 8. Module initialization function
PyMODINIT_FUNC
PyInit_simple_module(void) {
PyObject *m;
// Finalize the type object
if (PyType_Ready(&SimpleType) < 0) {
return NULL;
}
// Create the module
m = PyModule_Create(&simplemodule);
if (m == NULL) {
return NULL;
}
// Add the custom type to the module
Py_INCREF(&SimpleType); // Add a strong reference for the module
if (PyModule_AddObject(m, "SimpleObject", (PyObject *)&SimpleType) < 0) {
Py_DECREF(&SimpleType); // Decrement if adding fails
Py_DECREF(m);
return NULL;
}
return m;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment