我怎样才能从 Pyhon 端对存储在 std::vector 中的对象进行持久更改,如 std::shared_ptr
How can I make persistent changes from Pyhon's side to objects stored in std::vector's as std::shared_ptr's
假设我定义了两个 类(Foo
和 Bar
)。 Bar
在 std::vector
中存储两个 Foo
作为 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
让我用 pybind11 文档中的简单引文来回答,因为它在那里有很好的解释。
The major downside of these implicit conversions is that containers must be converted (i.e. copied) on every Python->C++ and C++->Python transition, which can have implications on the program semantics and performance. Please read the next sections for more details and alternative approaches that avoid this.
link: https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#stl-containers
By default, classes exported from C++ do not support this and the only writable attributes are the ones explicitly defined using class_::def_readwrite() or class_::def_property().
link: https://pybind11.readthedocs.io/en/stable/classes.html#dynamic-attributes
编辑:
bar.foos
returns copy std::vector
及其内部结构。如果您不存储返回的 python objects,您将在每次调用时得到 brand-new python objects。
是的,python Foo
包装器保存对原始 C++ 实例的引用(复数!)。但是动态属性是 python 包装器的属性,因此具有相同引用对象的不同 python 包装器不共享动态属性。
编辑 2:
你后面的片段有点令人费解。我想可能涉及 pybind11/cython 端的一些缓存。
更新:
回答问题标题:
How can I make persistent changes from Pyhon's side to objects stored in std::vector's as std::shared_ptr's
对 C++ 实例进行更改,而不是对其周围的 python 包装器进行更改。
假设我定义了两个 类(Foo
和 Bar
)。 Bar
在 std::vector
中存储两个 Foo
作为 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
让我用 pybind11 文档中的简单引文来回答,因为它在那里有很好的解释。
The major downside of these implicit conversions is that containers must be converted (i.e. copied) on every Python->C++ and C++->Python transition, which can have implications on the program semantics and performance. Please read the next sections for more details and alternative approaches that avoid this.
link: https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#stl-containers
By default, classes exported from C++ do not support this and the only writable attributes are the ones explicitly defined using class_::def_readwrite() or class_::def_property().
link: https://pybind11.readthedocs.io/en/stable/classes.html#dynamic-attributes
编辑:
bar.foos
returns copy std::vector
及其内部结构。如果您不存储返回的 python objects,您将在每次调用时得到 brand-new python objects。
是的,python Foo
包装器保存对原始 C++ 实例的引用(复数!)。但是动态属性是 python 包装器的属性,因此具有相同引用对象的不同 python 包装器不共享动态属性。
编辑 2:
你后面的片段有点令人费解。我想可能涉及 pybind11/cython 端的一些缓存。
更新: 回答问题标题:
How can I make persistent changes from Pyhon's side to objects stored in std::vector's as std::shared_ptr's
对 C++ 实例进行更改,而不是对其周围的 python 包装器进行更改。