使用 pybind11 混合类型转换和不透明类型
Mixing type conversions and opaque types with pybind11
我在 pybind11 中使用不透明类型。比如我为stl容器定义了一个std::vector<uint32_t>
,就是方法的一个参数类型FromVec
:
void FromVec(std::vector<uint32_t> vec);
PYBIND11_MAKE_OPAQUE(std::vector<uint32_t>);
PYBIND11_MODULE(tmap, m)
{
py::bind_vector<std::vector<uint32_t>>(m, "VectorUint",
"Unsigned 32-bit int vector.");
m.def("from_vec", &FromVec, py::arg("vec")
}
现在我可以(在 Python 中)执行以下操作:
vec = VectorUint([2, 66, 262, 662, 26, 62])
from_vec(vec)
但是,类型转换不再起作用,因为该函数现在需要 VectorUint
并且不再接受 list
,例如:
l = [2, 66, 262, 662, 26, 62]
from_vec(l)
有没有办法同时允许倾斜类型和类型转换?也就是说,当传递 list
而不是倾斜绑定 VectorUint
时,它会转换为 std::vector<uint32_t>
而不是 Python 抛出 "incompatible function arguments"?
如果您需要从 list
自动转换为 std::vector
,您可以将这样的重载添加到您的绑定代码中:
m.def("from_vec", &FromVec, py::arg("vec")); // accepts VectorUnit
m.def("from_vec", [](py::list& list){ /* do conversion manually and call FromVec */ } ); // accepts python list
只需在 py::bind_vector
:
之后添加
py::implicitly_convertible<py::list, std::vector<uint32_t>>();
它甚至可以使用属性(例如使用 def_readwrite
创建)。您获得了两全其美的优势:通过引用从 python 访问向量,因此 append
、extend
等按预期工作;您可以从 python 列表中分配(当然会被复制)。
编辑:不幸的是,这个技巧不适用于 std::map
(不知道其他容器)。作为解决方法,您可以使用不同类型的中间映射,例如:
namespace pybind11::workaround
{
template<typename K, typename V>
class Map : public std::map<K, V> {};
}
namespace pybind11::detail
{
template<typename K, typename V>
struct type_caster<workaround::Map<K, V>> : map_caster<workaround::Map<K, V>, K, V> {};
}
inline void bindCommonTypes(pybind11::handle scope)
{
using IntMap = std::map<int, int>;
py::bind_map<IntMap>(scope, "IntMap")
.def(py::init([](const py::dict& dict) {
return dict.cast<pybind11::workaround::Map<int, int>();
}));
py::implicitly_convertible<py::dict, IntMap>();
}
我在 pybind11 中使用不透明类型。比如我为stl容器定义了一个std::vector<uint32_t>
,就是方法的一个参数类型FromVec
:
void FromVec(std::vector<uint32_t> vec);
PYBIND11_MAKE_OPAQUE(std::vector<uint32_t>);
PYBIND11_MODULE(tmap, m)
{
py::bind_vector<std::vector<uint32_t>>(m, "VectorUint",
"Unsigned 32-bit int vector.");
m.def("from_vec", &FromVec, py::arg("vec")
}
现在我可以(在 Python 中)执行以下操作:
vec = VectorUint([2, 66, 262, 662, 26, 62])
from_vec(vec)
但是,类型转换不再起作用,因为该函数现在需要 VectorUint
并且不再接受 list
,例如:
l = [2, 66, 262, 662, 26, 62]
from_vec(l)
有没有办法同时允许倾斜类型和类型转换?也就是说,当传递 list
而不是倾斜绑定 VectorUint
时,它会转换为 std::vector<uint32_t>
而不是 Python 抛出 "incompatible function arguments"?
如果您需要从 list
自动转换为 std::vector
,您可以将这样的重载添加到您的绑定代码中:
m.def("from_vec", &FromVec, py::arg("vec")); // accepts VectorUnit
m.def("from_vec", [](py::list& list){ /* do conversion manually and call FromVec */ } ); // accepts python list
只需在 py::bind_vector
:
py::implicitly_convertible<py::list, std::vector<uint32_t>>();
它甚至可以使用属性(例如使用 def_readwrite
创建)。您获得了两全其美的优势:通过引用从 python 访问向量,因此 append
、extend
等按预期工作;您可以从 python 列表中分配(当然会被复制)。
编辑:不幸的是,这个技巧不适用于 std::map
(不知道其他容器)。作为解决方法,您可以使用不同类型的中间映射,例如:
namespace pybind11::workaround
{
template<typename K, typename V>
class Map : public std::map<K, V> {};
}
namespace pybind11::detail
{
template<typename K, typename V>
struct type_caster<workaround::Map<K, V>> : map_caster<workaround::Map<K, V>, K, V> {};
}
inline void bindCommonTypes(pybind11::handle scope)
{
using IntMap = std::map<int, int>;
py::bind_map<IntMap>(scope, "IntMap")
.def(py::init([](const py::dict& dict) {
return dict.cast<pybind11::workaround::Map<int, int>();
}));
py::implicitly_convertible<py::dict, IntMap>();
}