Как я могу сделать постоянные изменения со стороны Pyhon в объекты, хранящиеся в std :: vector как std :: shared_ptr - PullRequest
2 голосов
/ 01 октября 2019

Представьте, что я определяю два класса (Foo и Bar). Bar сохраняет два Foo с в std::vector как std::shared_ptr с. Я хочу показать все на Python с помощью pybind11. Кроме того, я хочу, чтобы Foo также поддерживал динамические атрибуты, что требует использования pybind11::dynamic_attr() на этапе привязки. Все работает нормально, пока я не пытаюсь добавлять динамические атрибуты к экземплярам Foo только через вектор, который их хранит. Поскольку нелегко объяснить мою проблему словами, вот MWE:

Модуль pybind11 определен в файле pyissue.cpp:

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

#include <vector>
#include <memory>

namespace py = pybind11;

class Foo {
public:
    Foo() {
        a = 5;
    }

    ~Foo() {

    }

    int a;
};

class Bar {
public:
    Bar() {
        foos.push_back(std::make_shared<Foo>());
        foos.push_back(std::make_shared<Foo>());
    }
    ~Bar() {

    }

    std::vector<std::shared_ptr<Foo>> foos;
};


PYBIND11_MODULE(pybug, m) {
    py::class_<Foo, std::shared_ptr<Foo>> foo(m, "Foo", py::dynamic_attr());
    foo
        .def(py::init<>())
        .def_readwrite("a", &Foo::a);

    py::class_<Bar> bar(m, "Bar");
    bar
        .def(py::init<>())
        .def_readwrite("foos", &Bar::foos);

, который можно скомпилироватьследующим образом (по крайней мере, на моем компьютере с Linux):

g++ pyissue.cpp -shared --std=c++11 -fPIC `python3 -m pybind11 --includes` -o pyissue`python3-config --extension-suffix`

Теперь следующий фрагмент кода Python работает точно так же, как и предполагалось:

import pyissue

bar = pyissue.Bar()
print(bar.foos[0].a) # prints 5

bar.foos[0].a = 2
print(bar.foos[0].a) # prints 2

myfoo = bar.foos[0]
myfoo.b = 3 # this is a dynamic attribute, it exists only on python's side
print(myfoo.b) # prints 3

Однако следующий фрагмент кода вызывает исключение AttributeError: 'pybug.Foo' object has no attribute 'b':

import pyissue

bar = pyissue.Bar()

bar.foos[0].b = 2
print(b.foos[0].b) # here comes the exception

Мой вопрос сводится к следующему: есть ли способ заставить последний фрагмент работать?

Редактировать : обратите внимание, что если я явно сохраню ссылку наобъект, то я могу использовать его без каких-либо проблем. Например, следующий код будет работать как положено:

import pyissue

bar = pyissue.Bar()

myfoo = bar.foos[0]
bar.foos[0].b = 2
print(b.foos[0].b) # prints 2

1 Ответ

1 голос
/ 01 октября 2019

Позвольте мне ответить голыми цитатами из документа pybind11, так как это довольно хорошо объяснено там.

Основным недостатком этих неявных преобразований является то, что контейнеры должны быть преобразованы (т.е. скопированы) при каждом переходе Python-> C ++ и C ++ -> Python, что может влиять на семантику и производительность программы. Пожалуйста, прочитайте следующие разделы для более подробной информации и альтернативных подходов, которые избегают этого.

ссылка: https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#stl-containers

По умолчанию классы, экспортируемые из C ++, не поддерживают это иединственными доступными для записи атрибутами являются те, которые явно определены с использованием класса _ :: def_readwrite () или класса _ :: def_property ().

ссылка: https://pybind11.readthedocs.io/en/stable/classes.html#dynamic-attributes

Редактировать:

bar.foos возвращает копию std::vector и его внутренних компонентов. Если вы не храните возвращенные объекты Python, вы будете получать совершенно новые объекты Python при каждом вызове.

Да, оболочки Python Foo содержат ссылки (множественное число!) На исходный экземпляр C ++. Но динамические атрибуты - это свойства оболочек Python, поэтому разные оболочки Python с одним и тем же референтом не разделяют динамические атрибуты.

Редактировать 2:

Ваш последний фрагмент немного озадачивает. Я предполагаю, что некоторое кэширование на стороне pybind11 / cython может быть вовлечено.

Обновление: Ответ на заголовок вопроса:

Как я могу сделать постоянные изменения со стороны Pyhon на объекты, хранящиеся в std :: vector как std:: shared_ptr

Вносить изменения в экземпляр C ++, а не в оболочку Python вокруг него.

...