我该如何从Pyhon端对存储在std :: vector中的对象进行永久更改,如std :: shared_ptr那样

时间:2019-10-01 08:59:42

标签: python c++ pybind11

想象一下,我定义了两个类(FooBar)。 Bar将两个Foo存储在std::vector中作为std::shared_ptr。我想使用pybind11将所有内容公开给Python。另外,我希望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 个答案:

答案 0 :(得分:1)

让我用pybind11 doc的裸露引用来回答,因为在那已经很好地解释了。

  

这些隐式转换的主要缺点是必须在每个Python-> C ++和C ++-> Python转换上转换(即复制)容器,这可能会对程序的语义和性能产生影响。请阅读下一部分,以了解更多详细信息和避免这种情况的替代方法。

链接:https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#stl-containers

  

默认情况下,从C ++导出的类不支持此功能,唯一可写的属性是使用class _ :: def_readwrite()或class _ :: 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包装器进行更改。