Вызов C/C++ из Python: как интегрировать нативный код в Python-проект?
Сравнение подходов и практические примеры интеграции C/C++ с Python
Вы выбрали правильный путь — ctypes отлично подходит для простых функций, но для сложных сценариев (массивы, классы, строки) лучше использовать более мощные инструменты. Рассмотрим три основных подхода с примерами.
1. ctypes — для простых C-функций
Ваш пример с fibonacci работает, но важно указывать типы аргументов и возвращаемого значения:
import ctypes
lib = ctypes.CDLL('./fibonacci.dll')
lib.fibonacci.argtypes = [ctypes.c_int]
lib.fibonacci.restype = ctypes.c_int
print(lib.fibonacci(10)) # 55
Для массивов используйте указатели:
// C++ функция: void multiply_array(int* arr, int size, int factor)
extern "C" {
void multiply_array(int* arr, int size, int factor) {
for (int i = 0; i < size; ++i)
arr[i] *= factor;
}
}
# Python вызов
import ctypes
lib = ctypes.CDLL('./mylib.dll')
lib.multiply_array.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_int, ctypes.c_int]
lib.multiply_array.restype = None
arr = (ctypes.c_int * 5)(1, 2, 3, 4, 5)
lib.multiply_array(arr, 5, 10)
print(list(arr)) # [10, 20, 30, 40, 50]
2. pybind11 — современный C++ интерфейс (рекомендуется)
Позволяет напрямую работать с классами, строками, STL-контейнерами. Установка: pip install pybind11.
// example.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
#include <string>
namespace py = pybind11;
class Calculator {
public:
Calculator() {}
int add(int a, int b) { return a + b; }
std::vector<int> process_vector(const std::vector<int>& input) {
std::vector<int> result;
for (int v : input) result.push_back(v * 2);
return result;
}
std::string greet(const std::string& name) {
return "Hello, " + name + "!";
}
};
PYBIND11_MODULE(mymodule, m) {
py::class_<Calculator>(m, "Calculator")
.def(py::init<>())
.def("add", &Calculator::add)
.def("process_vector", &Calculator::process_vector)
.def("greet", &Calculator::greet);
}
Компиляция (Linux/Mac):
c++ -O3 -Wall -shared -std=c++11 -fPIC \
$(python3 -m pybind11 --includes) \
example.cpp -o mymodule$(python3-config --extension-suffix)
Использование в Python:
import mymodule
calc = mymodule.Calculator()
print(calc.add(5, 3)) # 8
print(calc.process_vector([1,2,3])) # [2, 4, 6]
print(calc.greet("Python")) # Hello, Python!
3. CFFI — для C-совместимых библиотек
Альтернатива ctypes с более чистым синтаксисом:
from cffi import FFI
ffi = FFI()
ffi.cdef("""
int fibonacci(int n);
void multiply_array(int* arr, int size, int factor);
""")
lib = ffi.dlopen('./mylib.dll')
print(lib.fibonacci(10)) # 55
arr = ffi.new("int[]", [1, 2, 3, 4, 5])
lib.multiply_array(arr, 5, 10)
print(list(arr)) # [10, 20, 30, 40, 50]
Рекомендации по выбору
- ctypes — если нужно быстро вызвать пару C-функций без лишних зависимостей.
- pybind11 — для серьёзных проектов с классами, STL, исключениями. Лучшая производительность и удобство.
- CFFI — хороший компромисс между простотой и функциональностью.
- Cython — если вы готовы писать на гибридном языке и нужна максимальная скорость.
Для вашей задачи с массивами и структурами я рекомендую pybind11 — он позволит передавать списки Python напрямую и работать с C++ классами как с обычными объектами Python.
<<Альтернатива: Cython — пишем "почти Python", получаем C-скорость
Cython позволяет писать код, похожий на Python, который компилируется в C-расширение. Это отличный вариант, если вы не хотите писать обёртки отдельно, а готовы немного изменить стиль кода.
Пример для вашей задачи с массивами:
# fib_module.pyx
def fibonacci(int n):
cdef int a = 0, b = 1, temp
if n <= 1:
return n
for i in range(2, n+1):
temp = a + b
a = b
b = temp
return b
def multiply_array(arr, int factor):
cdef int i
cdef int size = len(arr)
cdef int* c_arr = &arr[0] # получаем указатель на данные
for i in range(size):
c_arr[i] *= factor
Компиляция через setup.py:
from setuptools import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("fib_module.pyx"))
Использование:
import fib_module
print(fib_module.fibonacci(10)) # 55
arr = [1, 2, 3, 4, 5]
fib_module.multiply_array(arr, 10)
print(arr) # [10, 20, 30, 40, 50]
Плюсы: не нужно писать C++ код отдельно, всё в одном файле, типизация опциональна. Минусы: сложнее отладка и работа с внешними C++ библиотеками. Подходит, если ваш C++ код невелик и вы хотите сохранить Python-подобный синтаксис.
Ответов пока нет.
Войдите чтобы оставить ответ