Created
June 18, 2025 13:17
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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