Skip to main content

Include/cpython/funcobject.h — function object internals

CPython stores every Python def statement as a PyFunctionObject. This header exposes the full C layout used by the interpreter, the accessor macros that the eval loop calls on every CALL opcode, the PyCFunction family of typedefs for built-in callables, and the PyMethodDef table that wires C functions into module or type method lists.

Map

LinesSymbolKindNotes
1-10guard + includesboilerplatedepends on funcobject.h (public)
12-35PyFunctionObjectstructfull field layout, see Reading below
37-42PyFunction_GET_CODEmacroreads func_code without incref
43-48PyFunction_GET_GLOBALSmacroreads func_globals without incref
49-54PyFunction_GET_CLOSUREmacroreads func_closure, may be NULL
56-70PyCFunction / PyCFunctionWithKeywordstypedefC-level call signatures
72-90PyMethodDefstructname, meth pointer, flags, doc
92-110CO_OPTIMIZED and friends#definecode-object flag bits
112-120PyFunction_NewWithQualNameC APIinternal constructor

Reading

PyFunctionObject layout

The struct holds every attribute visible on a Python function object. Key fields:

typedef struct {
PyObject_HEAD
PyObject *func_code; /* PyCodeObject — the bytecode */
PyObject *func_globals; /* dict — the defining module's globals */
PyObject *func_defaults; /* tuple or NULL */
PyObject *func_kwdefaults; /* dict or NULL */
PyObject *func_closure; /* tuple of cells or NULL */
PyObject *func_doc;
PyObject *func_name;
PyObject *func_dict;
PyObject *func_weakreflist;
PyObject *func_module;
PyObject *func_annotations;
PyObject *func_typeparams;
vectorcallfunc vectorcall;
} PyFunctionObject;

func_closure is a tuple of PyCellObject values. The closure is NULL for functions that capture no free variables, so callers must guard before indexing.

Accessor macros

The three GET macros return borrowed references and skip the type check that PyObject_GetAttr would perform, making them safe only inside the eval loop where the type is already guaranteed.

#define PyFunction_GET_CODE(func) (((PyFunctionObject *)func)->func_code)
#define PyFunction_GET_GLOBALS(func) (((PyFunctionObject *)func)->func_globals)
#define PyFunction_GET_CLOSURE(func) (((PyFunctionObject *)func)->func_closure)

PyCFunction typedefs and PyMethodDef

Built-in functions exposed to Python are typed as one of:

typedef PyObject *(*PyCFunction)(PyObject *self, PyObject *args);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *self,
PyObject *args,
PyObject *kwargs);

PyMethodDef binds a name string to one of these pointers, a flags word (METH_VARARGS, METH_FASTCALL, METH_NOARGS, etc.), and an optional doc string. A NULL-terminated array of PyMethodDef is what PyModule_Create iterates to populate a module's namespace.

gopy notes

  • objects/function.go mirrors PyFunctionObject as Function with the same field set. FuncCode, FuncGlobals, and FuncClosure map one-for-one.
  • The GET macros correspond to Go methods Code(), Globals(), and Closure() on *Function; all return unretained pointers matching the borrowed-ref semantics of the macros.
  • PyMethodDef is represented as objects.MethodDef; module initialization iterates the slice in objects/module.go the same way CPython's PyModule_Create iterates the C array.
  • CO_* flag bits are declared in compile/compiler.go as typed constants and checked during code-object construction.