Search code examples
pybind11

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


I'm binding a type my_type

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

as well as a std::vector<my_type> with

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

How can/should I declare MyTypeVector's __repr__ method here if I want its output to be a sequence of MyType.__repr__ for each object in the container?


Solution

  • JesseC helped a lot, but someone pointed out a weakness in that approach: it forces either the classes to define their own operator<<, or the programmer to define it in the bindings (which is a problem if the class has already defined an operator<<, but doesn't match what he or she wants as their __repr__ output). The core library shouldn't need to be aware that it's getting binded and therefore shouldn't be forced to implement such method. To that end, one can modify the operator<< on the std::vector to:

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

    along with the binding

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