如何从 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
s。我想通过使用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 doc 的裸露引用来回答,因为它在那里得到了很好的解释。
这些隐式转换的主要缺点是容器必须在每次 Python->C++ 和 C++->Python 转换时进行转换(即复制(,这可能会对程序语义和性能产生影响。请阅读以下部分,了解更多详细信息和避免这种情况的替代方法。
链接: https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#stl-containers
默认情况下,从C++导出的类不支持此功能,唯一可写的属性是使用 class_::d ef_readwrite(( 或 class_::d ef_property(( 显式定义的属性。
链接: https://pybind11.readthedocs.io/en/stable/classes.html#dynamic-attributes
编辑:
bar.foos
返回std::vector
及其内部的副本。如果不存储返回的 python 对象,则每次调用都会获得全新的 python 对象。
是的,pythonFoo
包装器保存对原始C++实例的引用(复数!(。但是动态属性是 python 包装器的属性,因此具有相同引用的不同 python 包装器不共享动态属性。
编辑 2:
你的后一个片段有点令人费解。我想可能会涉及pybind11/cython端的一些缓存。
更新:回答问题标题:
如何从 Pyhon 端对存储在 std::vector 中的对象进行持久更改,作为 std::shared_ptr
对实例进行更改C++而不是围绕它的 python 包装器。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- 引用 std::shared:ptr 以避免引用计数
- C++14 unique_ptr并使用已删除的函数'std::unique-ptr' unique_ptr错误
- 使用std :: String ptr的错误打印std :: String
- C++中的大小释放:全局运算符delete的正确行为是什么(void*ptr,std::size_t size)
- std::哈希表示无序映射中的唯一 PTR
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- 正确使用std智能指针以确保ptr安全
- 如何创建 std::string 包装器,它将 ptr 保留为 std::string 和 ptr 到创建该包装器实例的
- 为什么 std::string{ "const char ptr" } 有效?
- 为什么 gcc 4.9.0 中没有定义"void operator delete(void* ptr, std::size_t size) noexcept;"?
- 在 std::map 的值中使用非 ptr 是一种很好的做法吗
- 共享 PTR - C++:std::shared_ptr<T> 和 std::shared_ptr<T const> 有什么区别?