3.1 Other Calling Conventions

Earlier, I mentioned that METH_VARARGS was the default but not only choice for the ml_flags field in a PyMethodDef structure. The other choices allow keyword arguments to be supplied or certain common cases to be handled more efficiently.

flag meaning
METH_OLDARGS Obsolete; do not use this.
METH_KEYWORDS Means the function accepts keyword arguments.
METH_O Means the function takes a single argument.
METH_NOARGS Means the function takes no arguments.

(in addition one of the flags METH_STATIC or METH_CLASS can be bitwise or-ed with one of the above values to implement static or class methods - this is described in chapter 4, ``Defining New Types''. Python 2.4 adds another flag - METH_COEXIST - but this covers a very specialized sitation and can certainly be ignored at first).

Here's an example module that demonstrates the use of each of the conventions (except for the obsolete METH_OLDARGS):

#include <Python.h>

static char cc_doc[] = 
"This module demonstrates the various calling conventions that the\n\
Python/C API offers.";

static PyObject*
cc_varargs(PyObject *self, PyObject *args)
{
	return PyString_FromFormat("received %d arguments", 
				   PyTuple_Size(args));
}

static char cc_varargs_doc[] = 
"varargs(*args) -> string";

static PyObject*
cc_o(PyObject *self, PyObject *arg)
{
	return PyString_FromFormat("received 1 arguments of type %.500s", 
				   arg->ob_type->tp_name);
}

static char cc_o_doc[] = 
	"";

static PyObject*
cc_noargs(PyObject *self, PyObject *noarg)
{
	return PyString_FromFormat("received no arguments, noarg is %p",
				   noarg);
}

static char cc_noargs_doc[] = 
	"";

static PyObject*
cc_keywords(PyObject *self, PyObject *args, PyObject *kwargs)
{
	PyObject *arg;
	static char* kwlist[] = {"arg", NULL};
	
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:keywords", 
					 kwlist, &arg))
		return NULL;

	Py_INCREF(Py_None);
	return Py_None;
}

static char cc_keywords_doc[] = 
	"";

static PyMethodDef cc_methods[] = {
	{"varargs", 	cc_varargs,	METH_VARARGS,	cc_varargs_doc},
	{"o",		cc_o,		METH_O,		cc_o_doc},
	{"noargs",	cc_noargs,	METH_NOARGS,	cc_noargs_doc},
	{"keywords", 	(PyCFunction)cc_keywords,       
	METH_KEYWORDS,	cc_keywords_doc},
	{NULL, NULL}
};

PyMODINIT_FUNC
initcallingconventions(void)
{
	Py_InitModule3("callingconventions", cc_methods, cc_doc);
}

(this uses a C API function PyString_FromFormat that hasn't been mentioned yet; this function is rather like the C function sprintf except the result is a Python, not C, string. And there's no risk of buffer overflow).

As you can see from the code above, there's a qualitative difference between the METH_O and METH_NOARGS conventions and the METH_KEYWORD convention: the former two are specializations of METH_VARARGS that allow less code and more efficiency in common cases, whereas METH_KEYWORD allows more functionality (passing keyword arguments) at the cost of slightly more code.

Let's see the various conventions in use:

>>> from callingconventions import varargs, o, noargs, keywords
>>> varargs()
'received 0 arguments'
>>> varargs(1)
'received 1 arguments'
>>> varargs('a', 'b', None)
'received 3 arguments'
>>> varargs(a=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: varargs() takes no keyword arguments

>>> o()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: o() takes exactly one argument (0 given)
>>> o(1)
'received 1 arguments of type int'
>>> o('1')
'received 1 arguments of type str'
>>> o(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: o() takes exactly one argument (2 given)

>>> noargs()
'received no arguments, noarg is 0x0'
>>> noargs(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: noargs() takes no arguments (1 given)

>>> keywords(1)
>>> keywords(arg=1)
>>> keywords(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: keywords() takes exactly 1 argument (2 given)
>>> keywords(1,arg=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: keyword parameter 'arg' was given by position and by name
>>> keywords(blarg=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: keywords() takes exactly 1 argument (0 given)

(XXX the last error message is surely a bug that should be fixed? in fact vgetargskeywords looks due for a rewrite on many levels).

So which convention should you use? If METH_NOARGS applies to your situation, it's a no-brainer. In the case of one argument, it's probably more transparent (and even more likely to be more efficient) to do whatever checking you'd be getting PyArg_ParseTuple to do by hand.

For longer argument lists, whether to put the effort in to support keyword arguments isn't something that can be decided absolutely. Bear in mind that keyword arguments are most useful when you have a large-ish number of optional arguments which can be supplied indpendently (or are even mutually exclusive, but that's a slightly dubious design choice).

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