C-расширения позволяют писать высокопроизводительные модули на C, которые можно импортировать прямо в Python. Рассмотрим процесс создания такого расширения.
#include <Python.h>
// Функция, которая будет доступна из Python
static PyObject* hello_world(PyObject* self, PyObject* args) {
printf("Hello from C extension!\n");
Py_RETURN_NONE;
}
// Описание методов модуля
static PyMethodDef module_methods[] = {
{"hello_world", hello_world, METH_NOARGS, "Prints hello world"},
{NULL, NULL, 0, NULL} // Sentinel
};
// Описание модуля
static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"mymodule", // имя модуля
NULL, // документация
-1, // размер состояния модуля
module_methods
};
// Инициализация модуля
PyMODINIT_FUNC PyInit_mymodule(void) {
return PyModule_Create(&mymodule);
}
Для сборки нужно создать setup.py
:
from setuptools import setup, Extension
module = Extension(
'mymodule',
sources=['mymodule.c'],
)
setup(
name='MyModule',
version='1.0',
description='My C Extension',
ext_modules=[module],
)
Собираем командой:
python setup.py build_ext --inplace
static PyObject* add_numbers(PyObject* self, PyObject* args) {
int a, b;
// Парсим аргументы
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL; // Если аргументы неверные
}
return PyLong_FromLong(a + b);
}
Форматы аргументов:
"i"
- integer"f"
- float"s"
- string"O"
- произвольный Python объектstatic PyObject* risky_operation(PyObject* self, PyObject* args) {
if (error_condition) {
PyErr_SetString(PyExc_RuntimeError, "Something went wrong");
return NULL;
}
// Нормальное выполнение
Py_RETURN_NONE;
}
typedef struct {
PyObject_HEAD
/* Тип-специфичные поля */
int value;
} MyObject;
static PyTypeObject MyType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymodule.MyType",
.tp_basicsize = sizeof(MyObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
};
// В PyInit_mymodule:
PyType_Ready(&MyType);
PyModule_AddObject(module, "MyType", (PyObject*)&MyType);
PyPy
для JIT-компиляцииCython
или Numba
from cffi import FFI
ffi = FFI()
ffi.cdef("int add(int a, int b);")
lib = ffi.dlopen("./mylib.so")
print(lib.add(2, 3))
создание C-расширений требует хорошего знания Python C API, но позволяет достичь максимальной производительности. Для большинства задач лучше начинать с более простых решений вроде Cython или cffi, а к "чистым" C-расширениям переходить только при необходимости тонкой оптимизации.