Перейти к содержанию

Последнее обновление:2026-10-12

Ускорение Python с помощью pybind11

Обычно трудоёмкие операции мы хотим реализовывать на C++, а затем вызывать соответствующие интерфейсы из Python; или же из-за особенностей низкоуровневой библиотеки нужна поддержка внешнего Python API — тогда нам, как правило, нужно обеспечить доступ к C++ из Python. Как это реализовать?

Способов много; в этом разделе на примере pybind11 представлен полный шаблон проектной инженерии — если в дальнейшем возникнет такая потребность, можно взять его за основу и доработать.

Примечание: (версия для ленивых) весь код этого раздела также выложен в закрытом сообществе.

1. Раскрытие макроса

Предположим: возьмём в качестве примера довольно трудоёмкую сортировку — если мы хотим написать сортировку на C++, а затем использовать её из Python, как это сделать?

C++-программа довольно проста: наружу выставляем интерфейс, внутри которого делается сортировка, например:

std::vector<int> sort_vector(const std::vector<int>& input) {
  std::vector<int> result = input;
  std::sort(result.begin(), result.end());
  return result;
}
Но вот вопрос: как использовать этот интерфейс с Pybind11?

Достаточно определить модуль с помощью макроса pybind11, например:

PYBIND11_MODULE(sort_module, m) {
    m.doc() = "pybind11 example plugin"; // Optional module docstring

    m.def("sort_vector", &sort_vector, "A function that sorts a list of integers");
}
После раскрытия макроса получается: PyInit_sort_module

Когда мы в python делаем import xxx-библиотеки, интерпретатор Python автоматически вызывает функцию-вход при загрузке расширения, написанного на C/C++, и этой функцией-входом является приведённый ниже PyInit_sort_module:

static ::pybind11::module_::module_def pybind11_module_def_sort_module [[maybe_unused]];

[[maybe_unused]] static void pybind11_init_sort_module(::pybind11::module_ &);

extern "C" [[maybe_unused]] __attribute__((visibility("default"))) PyObject *PyInit_sort_module();

extern "C" __attribute__((visibility("default"))) PyObject *PyInit_sort_module() {
    {
        const char *compiled_ver = "3" "." "11";
        const char *runtime_ver = Py_GetVersion();
        size_t len = std::strlen(compiled_ver);

        if (std::strncmp(runtime_ver, compiled_ver, len) != 0 || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) {
            PyErr_Format(PyExc_ImportError,
                         "Python version mismatch: module was compiled for Python %s, "
                         "but the interpreter version is incompatible: %s.",
                         compiled_ver, runtime_ver);
            return nullptr;
        }
    }

    pybind11::detail::get_internals();
    auto m = ::pybind11::module_::create_extension_module("sort_module", nullptr, &pybind11_module_def_sort_module);

    try {
        pybind11_init_sort_module(m);
        return m.ptr();
    } catch (pybind11::error_already_set & e) {
        pybind11::raise_from(e, PyExc_ImportError, "initialization failed");
        return nullptr;
    } catch (const std::exception &e) {
        ::pybind11::set_error(PyExc_ImportError, e.what());
        return nullptr;
    }
}

void pybind11_init_sort_module(::pybind11::module_ & m) {
    m.doc() = "pybind11 example plugin";

    m.def("sort_vector", &sort_vector, "A function that sorts a list of integers");
}
Поэтому, если не использовать этот макрос, при import библиотеки будет ошибка:

>>> import sort_module
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: dynamic module does not define module export function (PyInit_sort_module)

2. CMake + pybind11

Использование pybind11 в C++ выглядит так:

  • Заголовочные файлы

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
- Макрос

PYBIND11_MODULE(sort_module, m) {
    // 你的实现
}
Как же скачать и слинковать pybind11 в cmake?

Достаточно использовать модуль FetchContent, например:

FetchContent_Declare(
    pybind11
    URL https://github.com/pybind/pybind11/archive/v2.12.0.tar.gz
    )
Затем добавьте ваш модуль в CMakeLists:

pybind11_add_module(sort_module sort_module.cc)
cmake сам скачает pybingd11 и заодно соберёт его.

3. Использование

Выше будет сгенерирован so, установленный в каталог site-packages python'а, и при import он будет распознан; ниже демонстрация нашего собственного модуля:

import sort_module

def test_sort():
    input_list = [5, 2, 3, 1, 4]
    sorted_list = sort_module.sort_vector(input_list)
    print(f"Original list: {input_list}")
    print(f"Sorted list: {sorted_list}")

if __name__ == "__main__":
    test_sort()
Запуск:

(base) ➜  build python ../test.py
Original list: [5, 2, 3, 1, 4]
Sorted list: [1, 2, 3, 4, 5]

Похожие темы

Комментарии