Pybind11, как вызвать __repr__ объекта в std :: vector? - PullRequest
0 голосов
/ 16 июня 2020

Я привязываю тип 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");

Как я могу / должен объявить здесь метод MyTypeVector __repr__, если Я хочу, чтобы его вывод был последовательностью MyType.__repr__ для каждого объекта в контейнере?

Ответы [ 2 ]

1 голос
/ 17 июня 2020

это на самом деле очень просто. py::bind_vector - это просто оболочка вокруг class_, поэтому вы можете добавлять к нему методы, как если бы вы добавляли их в обычный класс.

В вашем случае вы можете просто сделать

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

Поэтому для создания строкового представления я обычно определяю методы toString и оператор << в моих классах c ++.

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 </p>

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(); 
})

или, в случае вашего настраиваемого класса, как

.def("__repr__", &::my_type::toString)
0 голосов
/ 17 июня 2020

Джесси C очень помог, но кто-то указал на слабость этого подхода: он заставляет классы определять свой собственный оператор << или программист определять его в привязках (что является проблемой, если class уже определил оператор <<, но не соответствует тому, что он или она хочет, как их результат <code>__repr__). Базовая библиотека не должна знать, что она привязывается, и поэтому не должна быть принуждена к реализации такого метода. С этой целью можно изменить оператор << в std :: vector на: </p>

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);
});
...