多态性和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&lt;unique_ptr&lt;MyBase&gt;&gt;,它就可以工作。我不知道为什么 pybind11 的原始指针有问题。 谢谢!听起来不错!您能否发布您的解决方案,以便我查看并声明为官方解决方案? 【参考方案1】:

我不知道为什么,但pybind11 似乎对mylist() 中的原始指针有问题。如果您将返回类型更改为vector&lt;unique_ptr&lt;MyBase&gt;&gt;,该示例将正常工作。以下示例编译为 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的主要内容,如果未能解决你的问题,请参考以下文章

C++11 智能指针和多态性

第十一周 继承与多态

TJI读书笔记11-多态

11多态&动态静态绑定(java)

阿里无线11.11 之 Weex——关于移动端动态性的思考实现和未来

JavaSE第11篇:面向对象之接口多态