多态性和pybind11
Posted
技术标签:
【中文标题】多态性和pybind11【英文标题】:Polymorphism and pybind11 【发布时间】:2018-04-03 15:37:05 【问题描述】:当我想在 Python 中使用 C++ 多态性时,我对 pybind11 有一个奇怪的行为。这是我的问题的一个简单示例:
import polymorphism as plm
a = plm.mylist()
print(a)
a[0].print()
a[1].print()
这个脚本的输出是
[MyBase, MyDerived]
我的基地
我的基地
但预期的输出是
[MyBase, MyDerived]
我的基地
我的派生
因为 mylist 返回一个 std::vector,其中包含派生类 (MyDerived) 的实例作为第二个成员。奇怪的是,当我打印整个列表时,MyDerived 被识别出来了。
这里是C++代码的头文件:
/* polymorphism.hpp */
#ifndef POLYMORPHISM_HPP
#define POLYMORPHISM_HPP
#include <vector>
class MyBase
public:
virtual void print() const;
;
class MyDerived : public MyBase
public:
virtual void print() const;
;
std::vector<MyBase*> mylist();
#endif
这里是cpp文件:
#include "polymorphism.hpp"
#include <iostream>
#include <pybind11/stl.h>
#include <pybind11/pybind11.h>
void MyBase::print() const
std::cout << "MyBase" << std::endl;
void MyDerived::print() const
std::cout << "MyDerived" << std::endl;
std::vector<MyBase*> mylist()
std::vector<MyBase*> list(2);
list[0] = new MyBase();
list[1] = new MyDerived();
return list;
PYBIND11_MODULE(polymorphism, m)
pybind11::class_<MyBase>(m, "MyBase")
.def(pybind11::init<>())
.def("print", &MyBase::print)
.def("__repr__", [](const MyBase &a) return "MyBase"; );
pybind11::class_<MyDerived, MyBase>(m, "MyDerived")
.def(pybind11::init<>())
.def("print", &MyDerived::print)
.def("__repr__", [](const MyDerived &a) return "MyDerived"; );
m.def("mylist", &mylist, "return a list");
编辑:更令人惊讶的是,当我删除 MyDerived 的“打印”绑定时,我收到以下错误消息
[MyBase, MyDerived]
我的基地
Traceback(最近一次通话最后一次):
文件“test.py”,第 8 行,在
a[1].print()
AttributeError: 'polymorphism.MyDerived' 对象没有属性 'print'
这条消息似乎意味着 MyDerived 被很好地识别,而错误版本的 print 被调用(如果我理解得很好的话)。
编辑 2:这是一个使用蹦床类的版本。但是,这个版本导致同样的错误输出。
/* polymorphism.hpp */
#ifndef POLYMORPHISM_HPP
#define POLYMORPHISM_HPP
#include <vector>
#include <pybind11/stl.h>
#include <pybind11/pybind11.h>
class MyBase
public:
virtual void print() const;
;
class MyDerived : public MyBase
public:
virtual void print() const;
;
std::vector<MyBase*> mylist();
class PyMyBase : public MyBase
public:
using MyBase::MyBase; // Inherit constructors
void print() const override PYBIND11_OVERLOAD(void, MyBase, print );
;
class PyMyDerived : public MyDerived
public:
using MyDerived::MyDerived; // Inherit constructors
void print() const override PYBIND11_OVERLOAD(void, MyDerived, print);
;
#endif
下面是对应的cpp文件:
/* polymorphism.cpp */
#include "polymorphism.hpp"
#include <iostream>
void MyBase::print() const
std::cout << "MyBase" << std::endl;
void MyDerived::print() const
std::cout << "MyDerived" << std::endl;
std::vector<MyBase*> mylist()
std::vector<MyBase*> list(2);
list[0] = new MyBase();
list[1] = new MyDerived();
return list;
PYBIND11_MODULE(polymorphism, m)
pybind11::class_<MyBase, PyMyBase>(m, "MyBase")
.def(pybind11::init<>())
.def("print", &MyBase::print)
.def("__repr__", [](const MyBase &a) return "MyBase"; );
pybind11::class_<MyDerived, PyMyDerived>(m, "MyDerived")
.def(pybind11::init<>())
.def("print", &MyDerived::print)
.def("__repr__", [](const MyDerived &a) return "MyDerived"; );
m.def("mylist", &mylist, "return a list");
【问题讨论】:
pybind11 文档说你需要一个所谓的蹦床类:pybind11.readthedocs.io/en/stable/advanced/classes.html 哦,是的,我也尝试过这个解决方案,但它不起作用。我编辑了我的信息,以便在您发现问题时将其提供给您。 也许指针在 python 中没有很好地转换,所以多态性不能工作,就好像我的向量包含值而不是指针,即它包含 MyBase 的实例而不是 MyBase* 的实例。这将解释为什么 MyDerived 在调用错误版本的 print 时被很好地识别。 我只是玩了一下,发现如果将mylist()
的返回类型更改为vector<unique_ptr<MyBase>>
,它就可以工作。我不知道为什么 pybind11 的原始指针有问题。
谢谢!听起来不错!您能否发布您的解决方案,以便我查看并声明为官方解决方案?
【参考方案1】:
我不知道为什么,但pybind11
似乎对mylist()
中的原始指针有问题。如果您将返回类型更改为vector<unique_ptr<MyBase>>
,该示例将正常工作。以下示例编译为 python 模块 example
并产生预期的输出。
example.cpp:
#include <pybind11/stl.h>
#include <pybind11/pybind11.h>
#include <iostream>
#include <memory>
#include <vector>
class MyBase
public:
virtual void print() const
std::cout << "MyBase::print()" << std::endl;
;
class MyDerived : public MyBase
public:
virtual void print() const override
std::cout << "MyDerived::print()" << std::endl;
;
std::vector<std::unique_ptr<MyBase>> mylist()
std::vector<std::unique_ptr<MyBase>> v;
v.push_back(std::make_unique<MyBase>());
v.push_back(std::make_unique<MyDerived>());
return v;
PYBIND11_MODULE(example, m)
pybind11::class_<MyBase>(m, "MyBase")
.def(pybind11::init<>())
.def("print", &MyBase::print)
.def("__repr__", [](MyBase const&) return "MyBase"; );
pybind11::class_<MyDerived>(m, "MyDerived")
.def(pybind11::init<>())
.def("print", &MyDerived::print)
.def("__repr__", [](MyDerived const&) return "MyDerived"; );
m.def("mylist", &mylist, "returns a list");
python 外壳:
>>> import example
>>> l = example.mylist()
>>> l[0].print()
MyBase::print()
>>> l[1].print()
MyDerived::print()
【讨论】:
以上是关于多态性和pybind11的主要内容,如果未能解决你的问题,请参考以下文章