pvdocs /Python/Calling C from Python

Calling C from Python

When you need maximum performance or need to use existing C/C++ libraries, Python provides several ways to interface with C code.

1. ctypes — Call Compiled Libraries

ctypes is in the standard library and lets you call functions in shared libraries (.so / .dll) directly.

import ctypes

# Load a shared library
lib = ctypes.CDLL("./mylib.so")          # Linux/macOS
# lib = ctypes.CDLL("mylib.dll")         # Windows
# libc = ctypes.CDLL("libc.so.6")        # system libc

# Call a simple C function
lib.add.argtypes = [ctypes.c_int, ctypes.c_int]
lib.add.restype  = ctypes.c_int
result = lib.add(3, 4)   # 7
// mylib.c → compile with: gcc -shared -fPIC -o mylib.so mylib.c
int add(int a, int b) {
    return a + b;
}

Common ctypes Types

C type ctypes
int c_int
double c_double
char * c_char_p
void * c_void_p
pointer POINTER(type)

2. cffi — C Foreign Function Interface

More ergonomic than ctypes; define the C API as a string.

from cffi import FFI
ffi = FFI()

ffi.cdef("""
    int add(int a, int b);
""")

lib = ffi.dlopen("./mylib.so")
lib.add(3, 4)  # 7

3. Python C Extension (C API)

Write a C module that Python imports natively — maximum performance and control.

// mymodule.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>

static PyObject* py_add(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
    return PyLong_FromLong(a + b);
}

static PyMethodDef methods[] = {
    {"add", py_add, METH_VARARGS, "Add two integers."},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef module = {
    PyModuleDef_HEAD_INIT, "mymodule", NULL, -1, methods
};

PyMODINIT_FUNC PyInit_mymodule(void) {
    return PyModule_Create(&module);
}
# setup.py
from setuptools import setup, Extension
setup(ext_modules=[Extension("mymodule", sources=["mymodule.c"])])

# Build:  python setup.py build_ext --inplace
import mymodule
mymodule.add(3, 4)  # 7

4. Cython

Write Python-like code with C type annotations; compiles to C, then to a shared library.

# mymodule.pyx
def add(int a, int b) -> int:
    return a + b
cython mymodule.pyx           # generates mymodule.c
gcc -shared -fPIC ... -o mymodule.so mymodule.c

Used heavily by NumPy, Pandas, scikit-learn.

5. Quick Comparison

Method Ease of Use Performance Use Case
ctypes Easy Good Use existing .so/.dll
cffi Easy Good Use existing C libraries
C Extension Complex Best Performance-critical code
Cython Medium Very good Accelerate Python code
SWIG Medium Good Wrap large C/C++ libraries

Key Interview Points