Pybind11,如何在 std::vector 中调用对象的 __repr__?

Pybind11, how to invoke the __repr__ of an object within a std::vector?

我正在绑定一个类型my_type

py::class_<my_type, std::shared_ptr<my_type>>(m, "MyType")
        .def("__repr__", [](const my_type& o){return fmt::format("MyType: {}", o);});

以及 std::vector 和

py::bind_vector<std::vector<my_type>>(m, "MyTypeVector");

如果我希望它的输出是容器中每个对象的 MyType.__repr__ 序列,我如何 can/should 在此处声明 MyTypeVector 的 __repr__ 方法?

其实很简单。 py::bind_vector 只是 class_ 的包装器,因此您可以向其添加方法,就像将它们添加到普通的 class.

在你的情况下你可以这样做

py::bind_vector<std::vector<my_type>>(m, "MyTypeVector")
  .def("__repr__", [](const std::vector<my_type>& v) {// generate your string here;});

所以为了制作字符串表示,我通常在我的 c++ classes 中定义 toString 方法和 << 运算符。

class BadData
{
// lots of stuff going on and removed here
    virtual void
    putMembers(std::ostream& out) const
    {
      out << "msg=" << >msg;
      out << ", ";
      out << "stack=" << stack;
    }

    virtual std::string
    toString() const
    {
      std::ostringstream out;
      out << "BadData(";
      putMembers(out);
      out << ")";
      return out.str();
    }
}

inline
std::ostream &operator<<(std::ostream& stream, const BadData &item)
{
  stream << item.toString();
  return stream;
}

我们还为 stl 集合定义了运算符<<

template<class T> inline
std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
{
  std::ostringstream out;
  out << "Vector[";
  if (v.size() > 0) {
    for (auto ii = v.cbegin(); ii != v.cend() -1 ; ++ii) {
      out << *ii << ", ";
    }
    out << v.back();
  }
  out << "]";
  os << out.str();
  return os;
}

因此,一旦您定义了所有这些运算符,您的 __repr__ 方法就可以看起来像

.def("__repr__", [](const std::vector<my_type>& v) {
    std::stringstream stream;
    stream << v;
    return stream.str(); 
})

或者在您的自定义 class 的情况下,例如

.def("__repr__", &::my_type::toString)

JesseC 提供了很多帮助,但有人指出了该方法的一个弱点:它迫使 classes 定义他们自己的运算符 <<,或者程序员在绑定中定义它(这是如果 class 已经定义了一个运算符 <<,但与他或她想要的 __repr__ 输出不匹配,则会出现问题)。核心库不需要知道它正在绑定,因此不应该被迫实现这种方法。 为此,可以将 std::vector 上的 operator<< 修改为:

template<class T>
inline std::string vector_repr(const std::vector<T>& v){
    std::ostringstream out;
    out << "Vector[";

    auto py_vector = py::cast(v);
    const auto separator = ", ";
    const auto* sep = "";

    for( auto obj : py_vector ){
        out << sep << obj.attr("__repr__")();
        sep = separator;
    }
    out << "]";

    return out.str();
}

连同绑定

py::bind_vector<MyTypeVector>(m, "MyTypeVector")
    .def("__repr__", [](const MyTypeVector& v){
             return vector_repr(v);
});