4.2 Adding Data and Methods

For a less trivial example, consider the following Python code:

class Name(object):
    def __init__(self, forname='', surname=''):
        self.forname = forname
        self.surname = surname
    def name(self):
        return "%s %s"%(self.forname, self.surname)

This is a perfectly fine Python class with no particular reason to be rewritten in C, but as we need an example, that's exactly what we'll do:

#include <Python.h>
#include <structmember.h>

typedef struct {
	PyObject_HEAD
	PyObject *firstname;
	PyObject *lastname;
} NameObject;

static int
Name_init(NameObject *self, PyObject *args, PyObject *kwds)
{
	PyObject *firstname = Py_None;
	PyObject *lastname = Py_None;
	PyObject *tmp = NULL;

	static char *kwlist[] = {"firstname", "lastname", NULL};

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
					 &firstname, &lastname))
		return -1;

	tmp = self->firstname;
	if (firstname == Py_None) {
		self->firstname = PyString_FromString("");
	}
	else {
		Py_INCREF(firstname);
		self->firstname = firstname;
	}
	Py_XDECREF(tmp);

	tmp = self->lastname;
	if (lastname == Py_None) {
		self->lastname = PyString_FromString("");
	}
	else {
		Py_INCREF(lastname);
		self->lastname = lastname;
	}
	Py_XDECREF(tmp);

	return 0;
}

static PyMemberDef Name_members[] = {
	{"firstname", T_OBJECT_EX, offsetof(NameObject, firstname), 0,
	 "first name"},
	{"lastname", T_OBJECT_EX, offsetof(NameObject, lastname), 0,
	 "last name"},
	{NULL}  /* Sentinel */
};

static PyObject *
Name_name(NameObject *self, PyObject *unused)
{
	PyObject *args = NULL;
	PyObject *format = NULL;
	PyObject *result = NULL;

	format = PyString_FromString("%s %s");
	if (format == NULL)
		goto fail;

	if (self->firstname == NULL) {
		PyErr_SetString(PyExc_AttributeError, "firstname");
		goto fail;
	}

	if (self->lastname == NULL) {
		PyErr_SetString(PyExc_AttributeError, "lastname");
		goto fail;
	}

	args = Py_BuildValue("OO", self->firstname, self->lastname);
	if (args == NULL)
		goto fail;

	result = PyString_Format(format, args);

  fail:
	Py_XDECREF(format);
	Py_XDECREF(args);

	return result;
}

static PyMethodDef Name_methods[] = {
	{"name", (PyCFunction)Name_name, METH_NOARGS,
	 "Return the name, combining the firstname and lastname."
	},
	{NULL}
};

static char Name_doc[] = 
	"boo.";

static PyTypeObject NameObjectType = {
	PyObject_HEAD_INIT(NULL)
	0,				/* ob_size           */
	"name.Name",			/* tp_name           */
	sizeof(NameObject),		/* tp_basicsize      */
	0,				/* tp_itemsize       */
	0,				/* tp_dealloc        */
	0,				/* tp_print          */
	0,				/* tp_getattr        */
	0,				/* tp_setattr        */
	0,				/* tp_compare        */
	0,				/* tp_repr           */
	0,				/* tp_as_number      */
	0,				/* tp_as_sequence    */
	0,				/* tp_as_mapping     */
	0,				/* tp_hash           */
	0,				/* tp_call           */
	0,				/* tp_str            */
	0,				/* tp_getattro       */
	0,				/* tp_setattro       */
	0,				/* tp_as_buffer      */
	Py_TPFLAGS_DEFAULT,		/* tp_flags          */
	Name_doc,			/* tp_doc            */
	0,				/* tp_traverse       */
	0,				/* tp_clear          */
	0,				/* tp_richcompare    */
	0,				/* tp_weaklistoffset */
	0,				/* tp_iter           */
	0,				/* tp_iternext       */
	Name_methods,	     		/* tp_methods        */
	Name_members,			/* tp_members        */
	0,				/* tp_getset         */
	0,				/* tp_base           */
	0,				/* tp_dict           */
	0,				/* tp_descr_get      */
	0,				/* tp_descr_set      */
	0,				/* tp_dictoffset     */
	(initproc)Name_init,		/* tp_init           */
};

PyMODINIT_FUNC
initname(void)
{
	PyObject* m;

	NameObjectType.tp_new = PyType_GenericNew;
	if (PyType_Ready(&NameObjectType) < 0)
		return;

	m = Py_InitModule3("name", NULL,
			   "XXX");
	if (m == NULL)
		return;

	Py_INCREF(&NameObjectType);
	PyModule_AddObject(m, "Name", (PyObject *)&NameObjectType);
}

THIS DOCUMENT IS A DRAFT! Comments to mwh@python.net please.