GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing docs.python.org — Defining Extension Types: Tutorial Defining Extension Types: Tutorial¶Python allows the writer of a C extension module to define new types that
can be manipulated from Python code, much like the built-in The Basics¶The CPython runtime sees all Python objects as variables of type
PyObject*, which serves as a “base type” for all Python objects.
The So, if you want to define a new extension type, you need to create a new type object. This sort of thing can only be explained by example, so here’s a minimal, but
complete, module that defines a new type named Note What we’re showing here is the traditional way of defining static
extension types. It should be adequate for most uses. The C API also
allows defining heap-allocated extension types using the
#define PY_SSIZE_T_CLEAN #include <Python.h> typedef struct { PyObject_HEAD /* Type-specific fields go here. */ } CustomObject; static PyTypeObject CustomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom.Custom", .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = PyType_GenericNew, }; static int custom_module_exec(PyObject *m) { if (PyType_Ready(&CustomType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { return -1; } return 0; } static PyModuleDef_Slot custom_module_slots[] = { {Py_mod_exec, custom_module_exec}, // Just use this while using static types {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {0, NULL} }; static PyModuleDef custom_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "custom", .m_doc = "Example module that creates an extension type.", .m_size = 0, .m_slots = custom_module_slots, }; PyMODINIT_FUNC PyInit_custom(void) { return PyModuleDef_Init(&custom_module); } Now that’s quite a bit to take in at once, but hopefully bits will seem familiar from the previous chapter. This file defines three things:
The first bit is: typedef struct { PyObject_HEAD } CustomObject; This is what a Custom object will contain. Note There is no semicolon above after the Of course, objects generally store additional data besides the standard
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject; The second bit is the definition of the type object. static PyTypeObject CustomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom.Custom", .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = PyType_GenericNew, }; Note We recommend using C99-style designated initializers as above, to
avoid listing all the The actual definition of We’re going to pick it apart, one field at a time: .ob_base = PyVarObject_HEAD_INIT(NULL, 0) This line is mandatory boilerplate to initialize the .tp_name = "custom.Custom", The name of our type. This will appear in the default textual representation of our objects and in some error messages, for example: >>> "" + custom.Custom() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate str (not "custom.Custom") to str Note that the name is a dotted name that includes both the module name and the
name of the type within the module. The module in this case is .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, This is so that Python knows how much memory to allocate when creating
new Note If you want your type to be subclassable from Python, and your type has the same
We set the class flags to .tp_flags = Py_TPFLAGS_DEFAULT, All types should include this constant in their flags. It enables all of the members defined until at least Python 3.3. If you need further members, you will need to OR the corresponding flags. We provide a doc string for the type in .tp_doc = PyDoc_STR("Custom objects"), To enable object creation, we have to provide a .tp_new = PyType_GenericNew, Everything else in the file should be familiar, except for some code in
if (PyType_Ready(&CustomType) < 0) { return -1; } This initializes the if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { return -1; } This adds the type to the module dictionary. This allows us to create
>>> import custom >>> mycustom = custom.Custom() That’s it! All that remains is to build it; put the above code in a file called
[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" [project] name = "custom" version = "1" in a file called from setuptools import Extension, setup setup(ext_modules=[Extension("custom", ["custom.c"])]) in a file called $ python -m pip install . in a shell should produce a file That wasn’t so hard, was it? Of course, the current Custom type is pretty uninteresting. It has no data and doesn’t do anything. It can’t even be subclassed. Adding data and methods to the Basic example¶Let’s extend the basic example to add some data and methods. Let’s also make
the type usable as a base class. We’ll create a new module, #define PY_SSIZE_T_CLEAN #include <Python.h> #include <stddef.h> /* for offsetof() */ typedef struct { PyObject_HEAD PyObject *first; /* first name */ PyObject *last; /* last name */ int number; } CustomObject; static void Custom_dealloc(PyObject *op) { CustomObject *self = (CustomObject *) op; Py_XDECREF(self->first); Py_XDECREF(self->last); Py_TYPE(self)->tp_free(self); } static PyObject * Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CustomObject *self; self = (CustomObject *) type->tp_alloc(type, 0); if (self != NULL) { self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *) self; } static int Custom_init(PyObject *op, PyObject *args, PyObject *kwds) { CustomObject *self = (CustomObject *) op; static char *kwlist[] = {"first", "last", "number", NULL}; PyObject *first = NULL, *last = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, &first, &last, &self->number)) return -1; if (first) { Py_XSETREF(self->first, Py_NewRef(first)); } if (last) { Py_XSETREF(self->last, Py_NewRef(last)); } return 0; } static PyMemberDef Custom_members[] = { {"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0, "first name"}, {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0, "last name"}, {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"}, {NULL} /* Sentinel */ }; static PyObject * Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy)) { CustomObject *self = (CustomObject *) op; if (self->first == NULL) { PyErr_SetString(PyExc_AttributeError, "first"); return NULL; } if (self->last == NULL) { PyErr_SetString(PyExc_AttributeError, "last"); return NULL; } return PyUnicode_FromFormat("%S %S", self->first, self->last); } static PyMethodDef Custom_methods[] = { {"name", Custom_name, METH_NOARGS, "Return the name, combining the first and last name" }, {NULL} /* Sentinel */ }; static PyTypeObject CustomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom2.Custom", .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = Custom_new, .tp_init = Custom_init, .tp_dealloc = Custom_dealloc, .tp_members = Custom_members, .tp_methods = Custom_methods, }; static int custom_module_exec(PyObject *m) { if (PyType_Ready(&CustomType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { return -1; } return 0; } static PyModuleDef_Slot custom_module_slots[] = { {Py_mod_exec, custom_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {0, NULL} }; static PyModuleDef custom_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "custom2", .m_doc = "Example module that creates an extension type.", .m_size = 0, .m_slots = custom_module_slots, }; PyMODINIT_FUNC PyInit_custom2(void) { return PyModuleDef_Init(&custom_module); } This version of the module has a number of changes. The The object structure is updated accordingly: typedef struct { PyObject_HEAD PyObject *first; /* first name */ PyObject *last; /* last name */ int number; } CustomObject; Because we now have data to manage, we have to be more careful about object allocation and deallocation. At a minimum, we need a deallocation method: static void Custom_dealloc(PyObject *op) { CustomObject *self = (CustomObject *) op; Py_XDECREF(self->first); Py_XDECREF(self->last); Py_TYPE(self)->tp_free(self); } which is assigned to the .tp_dealloc = Custom_dealloc, This method first clears the reference counts of the two Python attributes.
Note The explicit cast to In existing code, or in previous versions of this tutorial,
you might see similar functions take a pointer to the subtype
object structure ( Custom_dealloc(CustomObject *self) { Py_XDECREF(self->first); Py_XDECREF(self->last); Py_TYPE(self)->tp_free((PyObject *) self); } ... .tp_dealloc = (destructor) Custom_dealloc, This does the same thing on all architectures that CPython supports, but according to the C standard, it invokes undefined behavior. We want to make sure that the first and last names are initialized to empty
strings, so we provide a static PyObject * Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CustomObject *self; self = (CustomObject *) type->tp_alloc(type, 0); if (self != NULL) { self->first = PyUnicode_FromString(""); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = PyUnicode_FromString(""); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *) self; } and install it in the .tp_new = Custom_new, The
Note
The self = (CustomObject *) type->tp_alloc(type, 0); Since memory allocation may fail, we must check the Note We didn’t fill the Note If you are creating a co-operative We also define an initialization function which accepts arguments to provide initial values for our instance: static int Custom_init(PyObject *op, PyObject *args, PyObject *kwds) { CustomObject *self = (CustomObject *) op; static char *kwlist[] = {"first", "last", "number", NULL}; PyObject *first = NULL, *last = NULL, *tmp; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, &first, &last, &self->number)) return -1; if (first) { tmp = self->first; Py_INCREF(first); self->first = first; Py_XDECREF(tmp); } if (last) { tmp = self->last; Py_INCREF(last); self->last = last; Py_XDECREF(tmp); } return 0; } by filling the .tp_init = Custom_init, The Unlike the if (first) { Py_XDECREF(self->first); Py_INCREF(first); self->first = first; } But this would be risky. Our type doesn’t restrict the type of the
To be paranoid and protect ourselves against this possibility, we almost always reassign members before decrementing their reference counts. When don’t we have to do this?
We want to expose our instance variables as attributes. There are a number of ways to do that. The simplest way is to define member definitions: static PyMemberDef Custom_members[] = { {"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0, "first name"}, {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0, "last name"}, {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"}, {NULL} /* Sentinel */ }; and put the definitions in the .tp_members = Custom_members, Each member definition has a member name, type, offset, access flags and documentation string. See the Generic Attribute Management section below for details. A disadvantage of this approach is that it doesn’t provide a way to restrict the
types of objects that can be assigned to the Python attributes. We expect the
first and last names to be strings, but any Python objects can be assigned.
Further, the attributes can be deleted, setting the C pointers to We define a single method, static PyObject * Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy)) { CustomObject *self = (CustomObject *) op; if (self->first == NULL) { PyErr_SetString(PyExc_AttributeError, "first"); return NULL; } if (self->last == NULL) { PyErr_SetString(PyExc_AttributeError, "last"); return NULL; } return PyUnicode_FromFormat("%S %S", self->first, self->last); } The method is implemented as a C function that takes a def name(self): return "%s %s" % (self.first, self.last) Note that we have to check for the possibility that our Now that we’ve defined the method, we need to create an array of method definitions: static PyMethodDef Custom_methods[] = { {"name", Custom_name, METH_NOARGS, "Return the name, combining the first and last name" }, {NULL} /* Sentinel */ }; (note that we used the and assign it to the .tp_methods = Custom_methods, Finally, we’ll make our type usable as a base class for subclassing. We’ve
written our methods carefully so far so that they don’t make any assumptions
about the type of the object being created or used, so all we need to do is
to add the .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, We rename Finally, we update our from setuptools import Extension, setup setup(ext_modules=[ Extension("custom", ["custom.c"]), Extension("custom2", ["custom2.c"]), ]) and then we re-install so that we can $ python -m pip install . Providing finer control over data attributes¶In this section, we’ll provide finer control over how the #define PY_SSIZE_T_CLEAN #include <Python.h> #include <stddef.h> /* for offsetof() */ typedef struct { PyObject_HEAD PyObject *first; /* first name */ PyObject *last; /* last name */ int number; } CustomObject; static void Custom_dealloc(PyObject *op) { CustomObject *self = (CustomObject *) op; Py_XDECREF(self->first); Py_XDECREF(self->last); Py_TYPE(self)->tp_free(self); } static PyObject * Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CustomObject *self; self = (CustomObject *) type->tp_alloc(type, 0); if (self != NULL) { self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *) self; } static int Custom_init(PyObject *op, PyObject *args, PyObject *kwds) { CustomObject *self = (CustomObject *) op; static char *kwlist[] = {"first", "last", "number", NULL}; PyObject *first = NULL, *last = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, &first, &last, &self->number)) return -1; if (first) { Py_SETREF(self->first, Py_NewRef(first)); } if (last) { Py_SETREF(self->last, Py_NewRef(last)); } return 0; } static PyMemberDef Custom_members[] = { {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"}, {NULL} /* Sentinel */ }; static PyObject * Custom_getfirst(PyObject *op, void *closure) { CustomObject *self = (CustomObject *) op; return Py_NewRef(self->first); } static int Custom_setfirst(PyObject *op, PyObject *value, void *closure) { CustomObject *self = (CustomObject *) op; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "The first attribute value must be a string"); return -1; } Py_SETREF(self->first, Py_NewRef(value)); return 0; } static PyObject * Custom_getlast(PyObject *op, void *closure) { CustomObject *self = (CustomObject *) op; return Py_NewRef(self->last); } static int Custom_setlast(PyObject *op, PyObject *value, void *closure) { CustomObject *self = (CustomObject *) op; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "The last attribute value must be a string"); return -1; } Py_SETREF(self->last, Py_NewRef(value)); return 0; } static PyGetSetDef Custom_getsetters[] = { {"first", Custom_getfirst, Custom_setfirst, "first name", NULL}, {"last", Custom_getlast, Custom_setlast, "last name", NULL}, {NULL} /* Sentinel */ }; static PyObject * Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy)) { CustomObject *self = (CustomObject *) op; return PyUnicode_FromFormat("%S %S", self->first, self->last); } static PyMethodDef Custom_methods[] = { {"name", Custom_name, METH_NOARGS, "Return the name, combining the first and last name" }, {NULL} /* Sentinel */ }; static PyTypeObject CustomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom3.Custom", .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = Custom_new, .tp_init = Custom_init, .tp_dealloc = Custom_dealloc, .tp_members = Custom_members, .tp_methods = Custom_methods, .tp_getset = Custom_getsetters, }; static int custom_module_exec(PyObject *m) { if (PyType_Ready(&CustomType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { return -1; } return 0; } static PyModuleDef_Slot custom_module_slots[] = { {Py_mod_exec, custom_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {0, NULL} }; static PyModuleDef custom_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "custom3", .m_doc = "Example module that creates an extension type.", .m_size = 0, .m_slots = custom_module_slots, }; PyMODINIT_FUNC PyInit_custom3(void) { return PyModuleDef_Init(&custom_module); } To provide greater control, over the static PyObject * Custom_getfirst(PyObject *op, void *closure) { CustomObject *self = (CustomObject *) op; Py_INCREF(self->first); return self->first; } static int Custom_setfirst(PyObject *op, PyObject *value, void *closure) { CustomObject *self = (CustomObject *) op; PyObject *tmp; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "The first attribute value must be a string"); return -1; } tmp = self->first; Py_INCREF(value); self->first = value; Py_DECREF(tmp); return 0; } The getter function is passed a The setter function is passed the We create an array of static PyGetSetDef Custom_getsetters[] = { {"first", Custom_getfirst, Custom_setfirst, "first name", NULL}, {"last", Custom_getlast, Custom_setlast, "last name", NULL}, {NULL} /* Sentinel */ }; and register it in the .tp_getset = Custom_getsetters, The last item in a We also remove the member definitions for these attributes: static PyMemberDef Custom_members[] = { {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"}, {NULL} /* Sentinel */ }; We also need to update the static int Custom_init(PyObject *op, PyObject *args, PyObject *kwds) { CustomObject *self = (CustomObject *) op; static char *kwlist[] = {"first", "last", "number", NULL}; PyObject *first = NULL, *last = NULL, *tmp; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, &first, &last, &self->number)) return -1; if (first) { tmp = self->first; Py_INCREF(first); self->first = first; Py_DECREF(tmp); } if (last) { tmp = self->last; Py_INCREF(last); self->last = last; Py_DECREF(tmp); } return 0; } With these changes, we can assure that the We also rename the module initialization function and module name in the
initialization function, as we did before, and we add an extra definition to the
Supporting cyclic garbage collection¶Python has a cyclic garbage collector (GC) that can identify unneeded objects even when their reference counts are not zero. This can happen when objects are involved in cycles. For example, consider: >>> l = [] >>> l.append(l) >>> del l In this example, we create a list that contains itself. When we delete it, it still has a reference from itself. Its reference count doesn’t drop to zero. Fortunately, Python’s cyclic garbage collector will eventually figure out that the list is garbage and free it. In the second version of the >>> import custom3 >>> class Derived(custom3.Custom): pass ... >>> n = Derived() >>> n.some_attribute = n To allow a #define PY_SSIZE_T_CLEAN #include <Python.h> #include <stddef.h> /* for offsetof() */ typedef struct { PyObject_HEAD PyObject *first; /* first name */ PyObject *last; /* last name */ int number; } CustomObject; static int Custom_traverse(PyObject *op, visitproc visit, void *arg) { CustomObject *self = (CustomObject *) op; Py_VISIT(self->first); Py_VISIT(self->last); return 0; } static int Custom_clear(PyObject *op) { CustomObject *self = (CustomObject *) op; Py_CLEAR(self->first); Py_CLEAR(self->last); return 0; } static void Custom_dealloc(PyObject *op) { PyObject_GC_UnTrack(op); (void)Custom_clear(op); Py_TYPE(op)->tp_free(op); } static PyObject * Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CustomObject *self; self = (CustomObject *) type->tp_alloc(type, 0); if (self != NULL) { self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *) self; } static int Custom_init(PyObject *op, PyObject *args, PyObject *kwds) { CustomObject *self = (CustomObject *) op; static char *kwlist[] = {"first", "last", "number", NULL}; PyObject *first = NULL, *last = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, &first, &last, &self->number)) return -1; if (first) { Py_SETREF(self->first, Py_NewRef(first)); } if (last) { Py_SETREF(self->last, Py_NewRef(last)); } return 0; } static PyMemberDef Custom_members[] = { {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"}, {NULL} /* Sentinel */ }; static PyObject * Custom_getfirst(PyObject *op, void *closure) { CustomObject *self = (CustomObject *) op; return Py_NewRef(self->first); } static int Custom_setfirst(PyObject *op, PyObject *value, void *closure) { CustomObject *self = (CustomObject *) op; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "The first attribute value must be a string"); return -1; } Py_XSETREF(self->first, Py_NewRef(value)); return 0; } static PyObject * Custom_getlast(PyObject *op, void *closure) { CustomObject *self = (CustomObject *) op; return Py_NewRef(self->last); } static int Custom_setlast(PyObject *op, PyObject *value, void *closure) { CustomObject *self = (CustomObject *) op; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "The last attribute value must be a string"); return -1; } Py_XSETREF(self->last, Py_NewRef(value)); return 0; } static PyGetSetDef Custom_getsetters[] = { {"first", Custom_getfirst, Custom_setfirst, "first name", NULL}, {"last", Custom_getlast, Custom_setlast, "last name", NULL}, {NULL} /* Sentinel */ }; static PyObject * Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy)) { CustomObject *self = (CustomObject *) op; return PyUnicode_FromFormat("%S %S", self->first, self->last); } static PyMethodDef Custom_methods[] = { {"name", Custom_name, METH_NOARGS, "Return the name, combining the first and last name" }, {NULL} /* Sentinel */ }; static PyTypeObject CustomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom4.Custom", .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, .tp_new = Custom_new, .tp_init = Custom_init, .tp_dealloc = Custom_dealloc, .tp_traverse = Custom_traverse, .tp_clear = Custom_clear, .tp_members = Custom_members, .tp_methods = Custom_methods, .tp_getset = Custom_getsetters, }; static int custom_module_exec(PyObject *m) { if (PyType_Ready(&CustomType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { return -1; } return 0; } static PyModuleDef_Slot custom_module_slots[] = { {Py_mod_exec, custom_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {0, NULL} }; static PyModuleDef custom_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "custom4", .m_doc = "Example module that creates an extension type.", .m_size = 0, .m_slots = custom_module_slots, }; PyMODINIT_FUNC PyInit_custom4(void) { return PyModuleDef_Init(&custom_module); } First, the traversal method lets the cyclic GC know about subobjects that could participate in cycles: static int Custom_traverse(PyObject *op, visitproc visit, void *arg) { CustomObject *self = (CustomObject *) op; int vret; if (self->first) { vret = visit(self->first, arg); if (vret != 0) return vret; } if (self->last) { vret = visit(self->last, arg); if (vret != 0) return vret; } return 0; } For each subobject that can participate in cycles, we need to call the
Python provides a static int Custom_traverse(PyObject *op, visitproc visit, void *arg) { CustomObject *self = (CustomObject *) op; Py_VISIT(self->first); Py_VISIT(self->last); return 0; } Note The Second, we need to provide a method for clearing any subobjects that can participate in cycles: static int Custom_clear(PyObject *op) { CustomObject *self = (CustomObject *) op; Py_CLEAR(self->first); Py_CLEAR(self->last); return 0; } Notice the use of the Note You could emulate PyObject *tmp; tmp = self->first; self->first = NULL; Py_XDECREF(tmp); Nevertheless, it is much easier and less error-prone to always
use The deallocator static void Custom_dealloc(PyObject *op) { PyObject_GC_UnTrack(op); (void)Custom_clear(op); Py_TYPE(op)->tp_free(op); } Finally, we add the .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, That’s pretty much it. If we had written custom Subclassing other types¶It is possible to create new extension types that are derived from existing
types. It is easiest to inherit from the built in types, since an extension can
easily use the In this example we will create a >>> import sublist >>> s = sublist.SubList(range(3)) >>> s.extend(s) >>> print(len(s)) 6 >>> print(s.increment()) 1 >>> print(s.increment()) 2 #define PY_SSIZE_T_CLEAN #include <Python.h> typedef struct { PyListObject list; int state; } SubListObject; static PyObject * SubList_increment(PyObject *op, PyObject *Py_UNUSED(dummy)) { SubListObject *self = (SubListObject *) op; self->state++; return PyLong_FromLong(self->state); } static PyMethodDef SubList_methods[] = { {"increment", SubList_increment, METH_NOARGS, PyDoc_STR("increment state counter")}, {NULL}, }; static int SubList_init(PyObject *op, PyObject *args, PyObject *kwds) { SubListObject *self = (SubListObject *) op; if (PyList_Type.tp_init(op, args, kwds) < 0) return -1; self->state = 0; return 0; } static PyTypeObject SubListType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "sublist.SubList", .tp_doc = PyDoc_STR("SubList objects"), .tp_basicsize = sizeof(SubListObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_init = SubList_init, .tp_methods = SubList_methods, }; static int sublist_module_exec(PyObject *m) { SubListType.tp_base = &PyList_Type; if (PyType_Ready(&SubListType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) { return -1; } return 0; } static PyModuleDef_Slot sublist_module_slots[] = { {Py_mod_exec, sublist_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {0, NULL} }; static PyModuleDef sublist_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "sublist", .m_doc = "Example module that creates an extension type.", .m_size = 0, .m_slots = sublist_module_slots, }; PyMODINIT_FUNC PyInit_sublist(void) { return PyModuleDef_Init(&sublist_module); } As you can see, the source code closely resembles the typedef struct { PyListObject list; int state; } SubListObject; The primary difference for derived type objects is that the base type’s
object structure must be the first value. The base type will already include
the When a Python object is a static int SubList_init(PyObject *op, PyObject *args, PyObject *kwds) { SubListObject *self = (SubListObject *) op; if (PyList_Type.tp_init(op, args, kwds) < 0) return -1; self->state = 0; return 0; } We see above how to call through to the This pattern is important when writing a type with custom
The static int sublist_module_exec(PyObject *m) { SubListType.tp_base = &PyList_Type; if (PyType_Ready(&SubListType) < 0) { return -1; } if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) { return -1; } return 0; } Before calling After that, calling Footnotes
© Copyright 2001 Python Software Foundation.
This page is licensed under the Python Software Foundation License Version 2. Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License. See History and License for more information. The Python Software Foundation is a non-profit corporation. Please donate. Last updated on Nov 12, 2026 (08:53 UTC). Found a bug? Created using Sphinx 9.1.1. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |