多态性和pybind11

Polymorphism and pybind11

本文关键字:pybind11 多态性      更新时间:2023-10-16

当我想在python中使用C 多态性时,我对Pybind11有一个奇怪的行为。这是我问题的简单示例:

import polymorphism as plm
a = plm.mylist()
print(a)
a[0].print()
a[1].print()

此脚本的输出是

[mybase,mydedived]

mybase

mybase

但是预期的输出为

[mybase,mydedived]

mybase

mydedived

因为MyList返回一个std :: vector,其中包含一个派生类(MyDedived(的实例作为第二个成员。奇怪的是,当我整体打印列表时,我会识别mydedived。

这是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");
}

编辑:更令人惊讶的是,当我删除mydedived的"打印"的绑定时,我会收到以下错误消息

[mybase,mydedived]

mybase

追溯(最近的最新电话(:

文件" test.py",第8行,

a [1] .print((

attributeError:'polymorpormist.myderived的'对象没有属性'print'

此消息似乎意味着在调用错误版本的印刷版本时,MyDedived已得到认可(如果我理解很好(。

编辑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似乎对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 shell:

>>> import example
>>> l = example.mylist()
>>> l[0].print()
MyBase::print()
>>> l[1].print()
MyDerived::print()