allocation and deallocation. At a minimum, we need a deallocation method::
static void
- Custom_dealloc(CustomObject *self)
+ Custom_dealloc(PyObject *op)
{
+ CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
- Py_TYPE(self)->tp_free((PyObject *) self);
+ Py_TYPE(self)->tp_free(self);
}
which is assigned to the :c:member:`~PyTypeObject.tp_dealloc` member::
- .tp_dealloc = (destructor) Custom_dealloc,
+ .tp_dealloc = Custom_dealloc,
This method first clears the reference counts of the two Python attributes.
:c:func:`Py_XDECREF` correctly handles the case where its argument is
be an instance of a subclass.
.. note::
- The explicit cast to ``destructor`` above is needed because we defined
- ``Custom_dealloc`` to take a ``CustomObject *`` argument, but the ``tp_dealloc``
- function pointer expects to receive a ``PyObject *`` argument. Otherwise,
- the compiler will emit a warning. This is object-oriented polymorphism,
- in C!
+
+ The explicit cast to ``CustomObject *`` above is needed because we defined
+ ``Custom_dealloc`` to take a ``PyObject *`` argument, as the ``tp_dealloc``
+ function pointer expects to receive a ``PyObject *`` argument.
+ By assigning to the the ``tp_dealloc`` slot of a type, we declare
+ that it can only be called with instances of our ``CustomObject``
+ class, so the cast to ``(CustomObject *)`` is safe.
+ This is object-oriented polymorphism, in C!
+
+ In existing code, or in previous versions of this tutorial,
+ you might see similar functions take a pointer to the subtype
+ object structure (``CustomObject*``) directly, like this::
+
+ 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 ``tp_new`` implementation::
initial values for our instance::
static int
- Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+ 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;
by filling the :c:member:`~PyTypeObject.tp_init` slot. ::
- .tp_init = (initproc) Custom_init,
+ .tp_init = Custom_init,
The :c:member:`~PyTypeObject.tp_init` slot is exposed in Python as the
:meth:`~object.__init__` method. It is used to initialize an object after it's
concatenation of the first and last names. ::
static PyObject *
- Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+ Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
+ CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
definitions::
static PyMethodDef Custom_methods[] = {
- {"name", (PyCFunction) Custom_name, METH_NOARGS,
+ {"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
getting and setting the :attr:`!first` attribute::
static PyObject *
- Custom_getfirst(CustomObject *self, void *closure)
+ Custom_getfirst(PyObject *op, void *closure)
{
+ CustomObject *self = (CustomObject *) op;
Py_INCREF(self->first);
return self->first;
}
static int
- Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
+ 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");
We create an array of :c:type:`PyGetSetDef` structures::
static PyGetSetDef Custom_getsetters[] = {
- {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
+ {"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
- {"last", (getter) Custom_getlast, (setter) Custom_setlast,
+ {"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
allow strings [#]_ to be passed::
static int
- Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+ 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;
participate in cycles::
static int
- Custom_traverse(CustomObject *self, visitproc visit, void *arg)
+ Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
+ CustomObject *self = (CustomObject *) op;
int vret;
if (self->first) {
vret = visit(self->first, arg);
in ``Custom_traverse``::
static int
- Custom_traverse(CustomObject *self, visitproc visit, void *arg)
+ Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
+ CustomObject *self = (CustomObject *) op;
Py_VISIT(self->first);
Py_VISIT(self->last);
return 0;
participate in cycles::
static int
- Custom_clear(CustomObject *self)
+ Custom_clear(PyObject *op)
{
+ CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
and ``Custom_clear``::
static void
- Custom_dealloc(CustomObject *self)
+ Custom_dealloc(PyObject *op)
{
- PyObject_GC_UnTrack(self);
- Custom_clear(self);
- Py_TYPE(self)->tp_free((PyObject *) self);
+ PyObject_GC_UnTrack(op);
+ (void)Custom_clear(op);
+ Py_TYPE(op)->tp_free(op);
}
Finally, we add the :c:macro:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
can be safely cast to both ``PyListObject *`` and ``SubListObject *``::
static int
- SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
+ SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
- if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
+ SubListObject *self = (SubListObject *) op;
+ if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
} CustomObject;
static void
-Custom_dealloc(CustomObject *self)
+Custom_dealloc(PyObject *op)
{
+ CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
- Py_TYPE(self)->tp_free((PyObject *) self);
+ Py_TYPE(self)->tp_free(self);
}
static PyObject *
}
static int
-Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
+ CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
};
static PyObject *
-Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
+ CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
}
static PyMethodDef Custom_methods[] = {
- {"name", (PyCFunction) Custom_name, METH_NOARGS,
+ {"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = Custom_new,
- .tp_init = (initproc) Custom_init,
- .tp_dealloc = (destructor) Custom_dealloc,
+ .tp_init = Custom_init,
+ .tp_dealloc = Custom_dealloc,
.tp_members = Custom_members,
.tp_methods = Custom_methods,
};
} CustomObject;
static void
-Custom_dealloc(CustomObject *self)
+Custom_dealloc(PyObject *op)
{
+ CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
- Py_TYPE(self)->tp_free((PyObject *) self);
+ Py_TYPE(self)->tp_free(self);
}
static PyObject *
}
static int
-Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
+ CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
};
static PyObject *
-Custom_getfirst(CustomObject *self, void *closure)
+Custom_getfirst(PyObject *op, void *closure)
{
+ CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}
static int
-Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
+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;
}
static PyObject *
-Custom_getlast(CustomObject *self, void *closure)
+Custom_getlast(PyObject *op, void *closure)
{
+ CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}
static int
-Custom_setlast(CustomObject *self, PyObject *value, void *closure)
+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;
}
static PyGetSetDef Custom_getsetters[] = {
- {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
+ {"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
- {"last", (getter) Custom_getlast, (setter) Custom_setlast,
+ {"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
static PyObject *
-Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+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", (PyCFunction) Custom_name, METH_NOARGS,
+ {"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = Custom_new,
- .tp_init = (initproc) Custom_init,
- .tp_dealloc = (destructor) Custom_dealloc,
+ .tp_init = Custom_init,
+ .tp_dealloc = Custom_dealloc,
.tp_members = Custom_members,
.tp_methods = Custom_methods,
.tp_getset = Custom_getsetters,
} CustomObject;
static int
-Custom_traverse(CustomObject *self, visitproc visit, void *arg)
+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(CustomObject *self)
+Custom_clear(PyObject *op)
{
+ CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
}
static void
-Custom_dealloc(CustomObject *self)
+Custom_dealloc(PyObject *op)
{
- PyObject_GC_UnTrack(self);
- Custom_clear(self);
- Py_TYPE(self)->tp_free((PyObject *) self);
+ PyObject_GC_UnTrack(op);
+ (void)Custom_clear(op);
+ Py_TYPE(op)->tp_free(op);
}
static PyObject *
}
static int
-Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
+ CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
};
static PyObject *
-Custom_getfirst(CustomObject *self, void *closure)
+Custom_getfirst(PyObject *op, void *closure)
{
+ CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}
static int
-Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
+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;
}
static PyObject *
-Custom_getlast(CustomObject *self, void *closure)
+Custom_getlast(PyObject *op, void *closure)
{
+ CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}
static int
-Custom_setlast(CustomObject *self, PyObject *value, void *closure)
+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;
}
static PyGetSetDef Custom_getsetters[] = {
- {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
+ {"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
- {"last", (getter) Custom_getlast, (setter) Custom_setlast,
+ {"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
static PyObject *
-Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+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", (PyCFunction) Custom_name, METH_NOARGS,
+ {"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
.tp_new = Custom_new,
- .tp_init = (initproc) Custom_init,
- .tp_dealloc = (destructor) Custom_dealloc,
- .tp_traverse = (traverseproc) Custom_traverse,
- .tp_clear = (inquiry) Custom_clear,
+ .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,
} SubListObject;
static PyObject *
-SubList_increment(SubListObject *self, PyObject *unused)
+SubList_increment(PyObject *op, PyObject *Py_UNUSED(dummy))
{
+ SubListObject *self = (SubListObject *) op;
self->state++;
return PyLong_FromLong(self->state);
}
static PyMethodDef SubList_methods[] = {
- {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
+ {"increment", SubList_increment, METH_NOARGS,
PyDoc_STR("increment state counter")},
{NULL},
};
static int
-SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
+SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
- if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
+ SubListObject *self = (SubListObject *) op;
+ if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
.tp_basicsize = sizeof(SubListObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .tp_init = (initproc) SubList_init,
+ .tp_init = SubList_init,
.tp_methods = SubList_methods,
};