Skip to main content

Objects/methodobject.c

cpython 3.14 @ ab2d84fe1023/Objects/methodobject.c

C-implemented callables. Every built-in function visible in Python — len, print, dict.get, list.append, str.join — is backed by a PyCFunctionObject. This struct holds a PyMethodDef* (name, C function pointer, calling-convention flags, docstring) together with a m_self pointer (the module or class instance the function is bound to) and an optional m_module reference for module-level functions.

The critical design decision in this file is the ml_flags dispatch table. Rather than a single universal C signature, CPython defines six calling conventions controlled by bit flags in PyMethodDef.ml_flags. The right convention for a given function is chosen at call time, not at definition time, so a single function pointer type (PyCFunction) covers them all via casting. The PyCMethod variant (PyCMethodObject) adds a mm_class field for methods that need to know the defining class, used by __init_subclass__ and classmethod descriptors.

Map

LinesSymbolRolegopy
1-100PyCFunction_NewEx, PyCFunction_New, PyCMethod_New, free-list allocAllocate a PyCFunctionObject; free-list backed for performance; PyCMethod_New also sets mm_class.objects/method.go
101-200PyCFunction_GetFunction, PyCFunction_GetSelf, PyCFunction_GetFlags, PyCFunction_GetModuleInline accessors returning the C function pointer, m_self, ml_flags, and m_module.objects/method.go
201-380meth_call, flag-based dispatchtp_call implementation; dispatches to the correct C signature based on ml_flags combinations.objects/method.go
381-500meth_vectorcall, PyCFunction_CallMethodObjArgsZero-copy vectorcall path for METH_FASTCALL; avoids tuple/dict allocation for positional-only calls.objects/method.go
501-600meth_repr, meth_hash, meth_richcompare, meth_traverse, meth_deallocRepr as <built-in function name> or <built-in method name of obj>; hash; equality; GC support.objects/method.go
601-700PyCFunction_Type, PyCMethod_TypeType object definitions; PyCMethod_Type is a subtype of PyCFunction_Type and adds mm_class in the member list.objects/method.go

Reading

PyCFunction_NewEx and the free-list (lines 1 to 100)

cpython 3.14 @ ab2d84fe1023/Objects/methodobject.c#L1-100

PyCFunction_NewEx is the primary constructor. It accepts the PyMethodDef, the self object (module or instance), and an optional module name string. Like PyMethod_New, it uses a per-interpreter free-list:

PyObject *
PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
{
PyCFunctionObject *op;
op = (PyCFunctionObject *)free_list;
if (op != NULL) {
free_list = (PyCFunctionObject *)(op->m_self);
numfree--;
(void)PyObject_INIT(op, &PyCFunction_Type);
}
else {
op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
if (op == NULL)
return NULL;
}
op->m_ml = ml;
op->m_self = Py_NewRef(self);
op->m_module = Py_XNewRef(module);
op->m_weakreflist = NULL;
op->vectorcall = meth_vectorcall;
_PyObject_GC_TRACK(op);
return (PyObject *)op;
}

PyCMethod_New extends this to also store mm_class — the class on which the method was defined. This is needed by __init_subclass__ which must receive the class being initialized, not just the instance.

Flag-based dispatch in meth_call (lines 201 to 380)

cpython 3.14 @ ab2d84fe1023/Objects/methodobject.c#L201-380

meth_call unpacks ml_flags and routes to the correct C calling convention. The six conventions and their C signatures are:

FlagC signature called
METH_NOARGSf(self, NULL)
METH_Of(self, single_arg)
METH_VARARGSf(self, args_tuple)
`METH_VARARGSMETH_KEYWORDS`
METH_FASTCALLf(self, args_array, nargs)
`METH_FASTCALLMETH_KEYWORDS`
static PyObject *
meth_call(PyObject *func, PyObject *args, PyObject *kwds)
{
PyCFunctionObject *f = (PyCFunctionObject *)func;
PyMethodDef *ml = f->m_ml;
PyObject *self = f->m_self;
int flags = ml->ml_flags;

if ((flags & METH_KEYWORDS) == 0 && kwds != NULL
&& PyDict_GET_SIZE(kwds) != 0)
{
PyErr_Format(PyExc_TypeError,
"%.200s() takes no keyword arguments", ml->ml_name);
return NULL;
}

switch (flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS
| METH_O | METH_KEYWORDS)) {
case METH_VARARGS:
return ml->ml_meth(self, args);
case METH_VARARGS | METH_KEYWORDS:
return ((PyCFunctionWithKeywords)ml->ml_meth)(self, args, kwds);
case METH_FASTCALL:
return _PyArg_CallUnboundMethod_FastCall(f, args);
/* ... remaining cases ... */
}
}

METH_NOARGS and METH_O are checked after extracting the tuple so they can validate the exact argument count and produce better error messages than a generic "wrong number of arguments".

Vectorcall path (lines 381 to 500)

cpython 3.14 @ ab2d84fe1023/Objects/methodobject.c#L381-500

meth_vectorcall is the tp_vectorcall slot. For METH_FASTCALL functions it passes the raw args array directly, skipping tuple creation entirely:

static PyObject *
meth_vectorcall(PyObject *func, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
PyCFunctionObject *cf = (PyCFunctionObject *)func;
PyMethodDef *ml = cf->m_ml;
PyObject *self = cf->m_self;
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
int flags = ml->ml_flags;

switch (flags & (METH_FASTCALL | METH_KEYWORDS | METH_NOARGS | METH_O)) {
case METH_FASTCALL:
/* No keyword arguments allowed. */
if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {
PyErr_Format(PyExc_TypeError, /* ... */);
return NULL;
}
return ((PyCFunctionFast)ml->ml_meth)(self, args, nargs);
case METH_FASTCALL | METH_KEYWORDS:
return ((PyCFunctionFastWithKeywords)ml->ml_meth)(
self, args, nargs, kwnames);
/* ... METH_NOARGS, METH_O, METH_VARARGS fall-through ... */
}
}

This is the path taken by CALL_BUILTIN_FAST in the specializing interpreter. For a call like len(x) the entire dispatch goes through vectorcall without any heap allocation.

gopy mirror

objects/method.go for the Go-level equivalent of PyCFunctionObject. In gopy, built-in functions are represented as Go closures wrapped by the BuiltinFunction type, so the ml_flags dispatch is replaced by Go's native function types. PyCMethod_Type maps to a field on the method struct that records the defining class when needed.

CPython 3.14 changes

PyCMethod_Type and PyCMethod_New stabilized in 3.9 for Py_mod_exec-style extension modules. Vectorcall (tp_vectorcall) for PyCFunctionObject added in 3.9. METH_FASTCALL | METH_KEYWORDS became the recommended convention for new built-in methods in 3.10. PyCFunction_GetModule added in 3.9. _Py_IDENTIFIER-based method tables removed in 3.13 in favor of statically initialized PyMethodDef arrays.