Разделение модулей pybind11 и проблемы с автоматическим преобразованием типов

У меня есть набор модулей, которые я написал на C++ и экспортировал в Python с помощью pybind11. Все эти модули можно использовать независимо, но они используют общий набор настраиваемых типов, определенных в служебной библиотеке.

В каждом модуле есть код вроде того, что ниже. Заголовок Color.hpp определяет типы, которые используются в служебной библиотеке.

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Color.hpp"

std::vector<Color> buncha_colors(int n, std::string &color) {
    std::vector<Color> out;
    for (;n-- > 0;) {
        out.push_back(Color(color));
    }
    return out;
}

PYBIND11_MODULE(pb11_example_module, m) {
    m.def("buncha_colors", &buncha_colors);
}

Конечно, это не работает. Pybind не знает, как выполнить преобразование типа для объекта Color. Ответ (или, надеюсь, нет) - определить класс Color как часть модуля. После этого pybind может выполнять автоматическое преобразование типов.

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Colors.hpp"

std::vector<Color> buncha_colors(int n, std::string &color) {
    std::vector<Color> out;
    for (;n-- > 0;) {
        out.push_back(Color(color));
    }
    return out;
}

PYBIND11_MODULE(pb11_example_module, m) {
    pybind11::class_<Color>(m, "Color")
        .def(pybind11::init<std::string&>())
        .def("name", &Color::name);
    m.def("buncha_colors", &buncha_colors);
}

В идеале я хотел бы сохранить все эти пользовательские типы утилит и связанные с ними функции в отдельном модуле от всех модулей, которые их используют. Но мне нужно было бы определить преобразования типов или иным образом ссылаться на них в каждом модуле, который его использует. Как мне это сделать? Мне не нужны pb11_example_module.Color, utils.Color и так далее. Я понятия не имею об их совместимости с одним, и это кажется неправильным.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
0
838
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это началось как редактирование, но оказалось моим ответом.

Это интересно. Используя первый пример, где Color не экспортируется в модуле pybind ...

$ python
>>> import pb11_example_module
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
TypeError: Unable to convert function return value to a Python type! The signature was
    (arg0: int, arg1: str) -> List[Color]
>>> import utils  # defines Color
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
'red'

Также работает импорт служебной библиотеки перед импортом модуля-примера. Изменение имени класса "Color" на другое также не нарушает использования, поэтому он должен получать преобразование типа из другого модуля с использованием сигнатуры типа.

Если преобразование типа для этого типа C++ определено перед использованием, автоматическое преобразование типа работает. Утилиты преобразования типов, которые pybind использует для Python, являются глобальными и просматриваются во время выполнения. Вы можете прочитать все об этом здесь. Вышеупомянутое решение загрузки преобразования типа для настраиваемого типа в любой момент перед использованием является поддерживаемым идиоматическим решением.

В качестве продолжения, я думаю, что более идеальное решение (в ваших привязках) - убедиться, что вы импортировали модуль с помощью py::module::import("my_package.my_depdendency"). В конечном итоге, поскольку вы выполняете привязку к тому глобальному реестру, который вы упомянули, импорт модулей имеет побочный эффект - регистрацию типа через RTTI. Если вы импортируете зависимость модуля в свои привязки, все готово. Вот пример теста PR +, который быстро завершается неудачно, если он забыт (для нашего кода): github.com/RobotLocomotion/drake/pull/14072/files

Eric Cousineau 30.09.2020 22:42

@EricCousineau Отличный совет!

ktb 01.10.2020 17:29

Другие вопросы по теме