Skip to main content

Overview

The Python/C API gives C and C++ programmers access to the Python interpreter at multiple levels. There are two primary use cases:
  1. Extension Modules - Write C modules that extend the Python interpreter with new functionality
  2. Embedding Python - Use Python as a component within a larger C/C++ application
The API is compatible with C11 and C++11 standards. You don’t need to enable special compiler modes.

Include Files

All Python/C API functionality requires including a single header:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
Always include Python.h before any standard headers. Python may define preprocessor definitions that affect standard headers on some systems.

What Gets Included

The Python.h header automatically includes:
  • <assert.h>
  • <inttypes.h>
  • <limits.h>
  • <math.h>
  • <stdarg.h>
  • <string.h>
  • <wchar.h>
All names defined by Python.h use the Py or _Py prefix. Names beginning with _Py are internal and should not be used by extension writers.

Objects and Reference Counts

Most Python/C API functions work with PyObject* - a pointer to an opaque type representing any Python object.

Key Concepts

  • All Python objects live on the heap - never declare automatic/static PyObject variables
  • Every object has a type and a reference count
  • The reference count tracks how many references to the object exist
  • When the reference count reaches zero, the object is deallocated

Basic Example

PyObject *item = PyList_GetItem(list, 0);  // Borrowed reference
if (!PyLong_Check(item)) {
    PyErr_SetString(PyExc_TypeError, "Expected integer");
    return NULL;
}
long value = PyLong_AsLong(item);
if (value == -1 && PyErr_Occurred()) {
    return NULL;  // Handle error
}

Error Handling

C programmers must explicitly check for errors. The Python/C API uses error indicators:
  • Functions return NULL (for pointers) or -1 (for integers) on error
  • An exception is set in thread-local storage
  • Check with PyErr_Occurred() or test return values

Error Handling Pattern

PyObject *result = PyObject_GetItem(dict, key);
if (result == NULL) {
    // Check for specific exception
    if (PyErr_ExceptionMatches(PyExc_KeyError)) {
        PyErr_Clear();
        // Handle missing key
        result = PyLong_FromLong(0);
    } else {
        // Propagate other exceptions
        return NULL;
    }
}
Always check return values! Unhandled errors can corrupt program state and cause mysterious failures.

Useful Macros

Type Checking

Py_ssize_t  // Signed integer type, same size as size_t

Utility Macros

Py_MIN(x, y)
macro
Return the smaller of two values
Py_MAX(x, y)
macro
Return the larger of two values
Py_ABS(x)
macro
Return absolute value (arguments may be evaluated multiple times)
Py_UNUSED(arg)
macro
Silence compiler warnings for unused function arguments
int func(int a, int Py_UNUSED(b)) { return a; }

Thread Safety

Python uses a Global Interpreter Lock (GIL) to protect internal state:
  • Most API functions require holding the GIL
  • Release the GIL for long-running operations
  • Reacquire before calling Python APIs
Py_BEGIN_ALLOW_THREADS
// Long computation without Python objects
Py_END_ALLOW_THREADS

Next Steps

Very High Level Layer

Execute Python code from C

Object Protocol

Work with Python objects

Reference Counting

Memory management fundamentals

Exception Handling

Handle and raise exceptions

See Also

Build docs developers (and LLMs) love