Pybind11中typedef类型与构造函数参数不兼容错误解决咨询
解决Pybind11绑定std::tuple衍生类型时的构造函数参数不兼容问题
问题重现
C++定义代码
typedef std::tuple<int, int, int> Type; typedef std::tuple<int, int, int, int> Shape; class Properties { public: Properties() {} Properties(Type t_, Shape s_) : t{t_}, s{s_} {} // 其他成员省略 private: Type t; Shape s; };
Pybind11绑定代码
PYBIND11_MODULE(pybind_test, m) { py::class_<Type>(m, "Type") .def(py::init<>()) .def(py::init<int, int, int>()); py::class_<Shape>(m, "Shape") .def(py::init<>()) .def(py::init<int, int, int, int>()); py::class_<Properties>(m, "Properties") .def(py::init<>()) .def(py::init<Type, Shape>()); // 其他绑定省略 }
Python调用代码
import pybind_test type_obj = pybind_test.Type(0,8,1) shape_obj = pybind_test.Shape(10,10,10,10) prop = pybind_test.Properties(type_obj, shape_obj)
错误信息
TypeError: __init__(): incompatible constructor arguments. The following argument types are supported: 1. pybind_test.Properties() 2. pybind_test.Properties(arg0: Tuple[int, int, int], arg1: Tuple[int, int, int, int])
原因分析
Type和Shape是std::tuple的typedef别名,在C++中它们与对应的std::tuple是同一类型,但用py::class_绑定后,Pybind11会将其包装成独立的Python类,而Properties构造函数的参数签名在Python中被解析为原生tuple类型,导致自定义的Type/Shape实例无法匹配参数类型。
解决方法
方法1:使用Pybind11专门的tuple绑定函数
替换py::class_为py::bind_tuple,该函数会自动处理std::tuple与Python原生tuple的兼容转换,同时保留类名:
PYBIND11_MODULE(pybind_test, m) { // 绑定std::tuple衍生类型 py::bind_tuple<Type>(m, "Type"); py::bind_tuple<Shape>(m, "Shape"); py::class_<Properties>(m, "Properties") .def(py::init<>()) .def(py::init<Type, Shape>()); // 其他绑定省略 }
此时Python调用无需修改,Type/Shape实例可以直接传递给Properties构造函数,同时也支持直接传入原生tuple。
方法2:移除自定义类型绑定,直接使用原生tuple
如果不需要在Python中保留Type/Shape类名,可直接移除它们的绑定代码,让Python传递原生tuple:
PYBIND11_MODULE(pybind_test, m) { py::class_<Properties>(m, "Properties") .def(py::init<>()) .def(py::init<const Type&, const Shape&>()); }
Python调用改为:
import pybind_test type_tuple = (0,8,1) shape_tuple = (10,10,10,10) prop = pybind_test.Properties(type_tuple, shape_tuple)
方法3:添加隐式转换规则
如果必须保留py::class_绑定的Type/Shape类,可通过py::implicitly_convertible添加类型转换规则,让Pybind11自动将自定义类实例转换为对应的tuple类型:
PYBIND11_MODULE(pybind_test, m) { py::class_<Type>(m, "Type") .def(py::init<>()) .def(py::init<int, int, int>()); // 添加Type到std::tuple的隐式转换 py::implicitly_convertible<Type, std::tuple<int, int, int>>(); py::class_<Shape>(m, "Shape") .def(py::init<>()) .def(py::init<int, int, int, int>()); // 添加Shape到std::tuple的隐式转换 py::implicitly_convertible<Shape, std::tuple<int, int, int, int>>(); py::class_<Properties>(m, "Properties") .def(py::init<>()) .def(py::init<Type, Shape>()); }
这种方式下原Python调用代码可以正常运行。
内容的提问来源于stack exchange,提问作者Liuuuuk
相关产品推荐
相关产品推荐

