Extending and Embedding Python, Release 3.9.5 Chapter 2. Creating extensions without third party tools 32 Finally, we update our$_{setup.py}$ file to build the new module: We rename PyInit_custom() to PyInit_custom2() , update the module name in the PyModuleDef struct, and update the full class name in the$_{PyTypeObject}$struct. .tp_flags =Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 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 Py_TPFLAGS_BASETYPE to our class flag definition: .tp_methods=Custom_methods, (note that we used the$_{METH_NOARGS}$ flag to indicate that the method is expecting no arguments other than self ) and assign it to the$_{tp_methods}$slot: Now that we’ve defined the method, we need to create an array of method definitions: Note that we have to check for the possibility that our$_{first}$ and$_{last}$members are$_{NULL}$ . This is because they can be deleted, in which case they are set to$_{NULL}$ . It would be better to prevent deletion of these attributes and to restrict the attribute values to be strings. We’ll see how to do that in the next section. The method is implemented as a C function that takes a$_{Custom}$(or$_{Custom}$subclass) instance as the first argument. Methods always take an instance as the first argument. Methods often take positional and keyword arguments as well, but in this case we don’t take any and don’t need to accept a positional argument tuple or keyword argument dictionary. This method is equivalent to the Python method: We define a single method, Custom.name() , that outputs the objects name as the concatenation of the first and last names. 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$_{NULL}$ . Even though we can make sure the members are initialized to non-$_{NULL}$ values, the members can be set to$_{NULL}$ if the attributes are deleted. Each member definition has a member name, type, offset, access flags and documentation string. See the Generic Attribute Management section below for details. static PyObject * Custom_name(CustomObject *self, PyObject*Py_UNUSED(ignored)) { 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", (PyCFunction) Custom_name, METH_NOARGS, "Return the name, combining the first and last name " }, { NULL } /* Sentinel */ }; def name(self): return " %s%s "%(self.first,self.last)