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);
});
我正在绑定一个类型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);
});