pybind11 для C++: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

pybind11: Создание Python-привязок для C++ кода

pybind11 — это легковесная, исключительно заголовочная библиотека для языка C++, которая позволяет создавать высокопроизводительные привязки (bindings) между кодом на C++ и Python. Она является современной и более удобной альтернативой библиотеке Boost.Python. Основная идея заключается в том, что вы пишете небольшой слой "склеивающего" кода на C++, используя макросы и шаблоны pybind11, который компилируется в динамическую библиотеку (файл .so или .pyd). Затем этот файл можно импортировать в Python как обычный модуль.

Зачем это нужно? В реальных проектах часто возникает необходимость объединить высокую производительность и низкоуровневый контроль C++ с гибкостью и богатой экосистемой Python. Вы можете написать критически важный для скорости код (численные алгоритмы, обработка изображений, работа с железом) на C++, а затем использовать его из Python, сохраняя при этом удобство прототипирования и анализа данных. pybind11 делает этот процесс максимально простым и интуитивным, автоматически обрабатывая преобразование типов, управление памятью и вызов исключений.

Ключевые преимущества pybind11: минимальные накладные расходы (overhead) при вызове функций, полная поддержка современного C++ (C++11 и новее), автоматическое преобразование стандартных контейнеров (std::vector, std::map) в списки и словари Python, а также простая и выразительная система регистрации классов, методов и свойств. Библиотека активно развивается и используется в таких крупных проектах, как PyTorch, TensorFlow и Open3D.

Установка

pybind11 — это заголовочная библиотека, поэтому её установка сводится к копированию заголовочных файлов в ваш проект. Самый простой и рекомендуемый способ — использовать менеджер пакетов pip, который установит библиотеку в систему и предоставит утилиту pybind11-config для получения путей компиляции:

pip install pybind11

После установки вы можете проверить, что всё работает, выполнив команду:

pybind11-config --includes

Она выведет флаги компилятора, необходимые для включения заголовочных файлов pybind11. Для компиляции модуля вам также понадобится компилятор C++ (g++, clang, MSVC) и Python-заголовки (обычно устанавливаются вместе с Python). Пример компиляции с использованием CMake или напрямую через g++:

c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

Быстрый старт: минимальный рабочий пример

Создайте файл example.cpp со следующим содержимым:

#include <pybind11/pybind11.h>

namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin";
    m.def("add", &add, "A function that adds two numbers");
}

Скомпилируйте его (замените python3 на ваш интерпретатор):

c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

Теперь вы можете использовать модуль из Python:

import example
result = example.add(3, 4)
print(result)  # Выведет: 7
print(example.__doc__)  # Выведет: pybind11 example plugin

Основные методы и классы pybind11

Ниже приведены ключевые функции и классы библиотеки, которые используются для создания привязок.

1. PYBIND11_MODULE(name, variable)

Сигнатура: PYBIND11_MODULE(name, variable) (макрос)

Что делает: Определяет точку входа для модуля Python. Создает функцию инициализации, которая автоматически вызывается при импорте модуля. name — это имя модуля (без кавычек), variable — имя переменной типа py::module_, которую вы будете использовать для регистрации функций и классов.

Пример:

PYBIND11_MODULE(mymodule, m) {
    m.doc() = "My awesome module";
}

2. module_::def(name, func, doc_string)

Сигнатура: py::module_ &def(const char *name, Func &&f, const char *doc = "")

Что делает: Регистрирует функцию C++ как функцию Python в модуле. Автоматически обрабатывает аргументы и возвращаемые значения, преобразуя их в типы Python.

Пример:

std::string greet(const std::string &name) {
    return "Hello, " + name + "!";
}

PYBIND11_MODULE(example, m) {
    m.def("greet", &greet, "Greets a person by name");
}

3. class_<T>(module, name, doc_string)

Сигнатура: py::class_<T>(py::handle scope, const char *name)

Что делает: Регистрирует класс C++ как класс Python. Позволяет добавлять методы, свойства, статические методы и определять конструкторы. Возвращает объект, который можно дальше настраивать.

Пример:

struct Pet {
    Pet(const std::string &name) : name(name) {}
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }
    std::string name;
};

PYBIND11_MODULE(example, m) {
    py::class_<Pet>(m, "Pet")
        .def(py::init<const std::string &>())
        .def("set_name", &Pet::setName)
        .def("get_name", &Pet::getName)
        .def_readwrite("name", &Pet::name);
}

4. class_::def_readwrite(name, member) и def_readonly(name, member)

Сигнатура: class_ &def_readwrite(const char *name, T Class::*pm)

Что делает: Автоматически создает Python-свойство (property) для чтения и записи (def_readwrite) или только для чтения (def_readonly) для публичного поля-члена класса C++.

Пример: (см. пример выше с .def_readwrite("name", &Pet::name))

5. class_::def_property(name, getter, setter)

Сигнатура: class_ &def_property(const char *name, Getter &&get, Setter &&set)

Что делает: Создает свойство Python с геттером и сеттером. Позволяет контролировать доступ к данным, выполнять валидацию или логику при чтении/записи.

Пример:

py::class_<Pet>(m, "Pet")
    .def(py::init<const std::string &>())
    .def_property("name", &Pet::getName, &Pet::setName);

6. class_::def_static(name, func)

Сигнатура: class_ &def_static(const char *name, Func &&f)

Что делает: Регистрирует статический метод класса C++ как статический метод класса Python.

Пример:

struct Calculator {
    static int multiply(int a, int b) { return a * b; }
};

PYBIND11_MODULE(example, m) {
    py::class_<Calculator>(m, "Calculator")
        .def_static("multiply", &Calculator::multiply);
}

# В Python: Calculator.multiply(3, 4)

7. enum_<T>(module, name)

Сигнатура: py::enum_<T>(py::handle scope, const char *name)

Что делает: Регистрирует перечисление C++ (enum или enum class) как перечисление Python. Значения автоматически становятся атрибутами класса перечисления.

Пример:

enum class Color { RED, GREEN, BLUE };

PYBIND11_MODULE(example, m) {
    py::enum_<Color>(m, "Color")
        .value("RED", Color::RED)
        .value("GREEN", Color::GREEN)
        .value("BLUE", Color::BLUE)
        .export_values(); // Делает RED, GREEN, BLUE доступными в пространстве имен модуля
}

8. module_::attr(name, value)

Сигнатура: module_ &attr(const char *name, object &&value)

Что делает: Устанавливает произвольный атрибут модуля. Может быть числом, строкой, списком, словарем или любым другим объектом Python. Полезно для задания констант или версий.

Пример:

PYBIND11_MODULE(example, m) {
    m.attr("PI") = 3.14159;
    m.attr("VERSION") = "1.0.0";
}

# В Python: print(example.PI)

9. object::cast<T>()

Сигнатура: template <typename T> T cast() const

Что делает: Преобразует объект pybind11 (например, аргумент функции, полученный как py::object) обратно в тип C++. Полезно, когда вы хотите принять "родной" Python-объект и преобразовать его вручную.

Пример:

void process_list(py::list lst) {
    for (auto item : lst) {
        int value = item.cast<int>();
        // Работаем с value
    }
}

PYBIND11_MODULE(example, m) {
    m.def("process_list", &process_list);
}

10. stl.h — Автоматическое преобразование контейнеров

Сигнатура: Не требуется явного вызова. Достаточно включить заголовок #include <pybind11/stl.h>.

Что делает: Включает автоматическое преобразование между стандартными контейнерами C++ (std::vector, std::map, std::set, std::pair) и их аналогами в Python (списки, словари, множества, кортежи). Это избавляет от необходимости писать код преобразования вручную.

Пример:

#include <pybind11/stl.h>
#include <vector>
#include <string>

std::vector<std::string> get_fruits() {
    return {"apple", "banana", "cherry"};
}

PYBIND11_MODULE(example, m) {
    m.def("get_fruits", &get_fruits);
}

# В Python: fruits = example.get_fruits()  # fruits — это список ['apple', 'banana', 'cherry']

Реальный практический пример: Обработка изображений

Рассмотрим пример создания модуля для быстрой обработки изображений. Допустим, у нас есть класс C++, который преобразует цветное изображение в оттенки серого, используя эффективные вычисления.

// image_processor.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <vector>
#include <cstdint>

namespace py = pybind11;

class ImageProcessor {
public:
    // Принимаем изображение как numpy массив (H, W, 3) типа uint8
    py::array_t<uint8_t> to_grayscale(py::array_t<uint8_t, py::array::c_style> input) {
        auto buf = input.request();
        if (buf.ndim != 3 || buf.shape[2] != 3) {
            throw std::runtime_error("Input must be a 3D array with 3 channels (RGB)");
        }

        size_t height = buf.shape[0];
        size_t width = buf.shape[1];
        auto *ptr = static_cast<uint8_t*>(buf.ptr);

        // Создаем выходной массив (H, W) типа uint8
        auto result = py::array_t<uint8_t>({height, width});
        auto res_buf = result.request();
        auto *res_ptr = static_cast<uint8_t*>(res_buf.ptr);

        // Алгоритм luminosity: 0.21 R + 0.72 G + 0.07 B
        for (size_t y = 0; y < height; ++y) {
            for (size_t x = 0; x < width; ++x) {
                size_t idx = (y * width + x) * 3;
                uint8_t r = ptr[idx];
                uint8_t g = ptr[idx + 1];
                uint8_t b = ptr[idx + 2];
                res_ptr[y * width + x] = static_cast<uint8_t>(
                    0.21f * r + 0.72f * g + 0.07f * b
                );
            }
        }
        return result;
    }

    // Простой порогов                    

Рекомендаций