假设有一个类MyArray
,它实现了一个SomeType
数组。它是用C ++编写的,并使用boost :: python包装到Python中。
BOOST_PYTHON_MODULE(my_array_module)
{
class_<MyArray>("MyArray")
// a few '.def's ...
.def("__getitem__", ???)
;
}
Python中的__getitem__
函数可以获取索引并返回SomeType
值,也可以获取切片对象并返回切片。
There是如何处理在C ++中重载的函数,将它们包装到不同的Python函数中。 There是如何在Python中创建重载函数,如果重载意味着不同数量的args。
但如果它们因arument类型而不同,如何包装重载函数?我需要在C ++中使用2个getitem
函数。
const SomeType& getitem(PyObject *self, size_t pos) {
// ...
}
MyArray getitem(PyObject *self, slice sl) {
// ...
}
如果您尝试使用描述为here的BOOST_PYTHON_FUNCTION_OVERLOADS
方式将其换行,则无法编译。
我可以创建一个函数
PyObject* getitem(PyObject *self, PyObject *pos_or_slice) {
extract<size_t> get_pos(pos_or_slice);
if (get_pos.check()) {
// return a single item (SomeType)
}
else {
// return a slice (MyArray)
}
}
但我不知道如何正确地将MyArray
包裹到PyObject*
中,这样会与class_
生成的包裹一致。
答案 0 :(得分:2)
简而言之,只要C ++函数具有不同的参数类型,那么每个函数都可以作为相同的Python函数公开,并单独调用def()
。 Boost.Python将根据类型转换处理调度。如果类型不明确,那么通常需要创建和公开一个辅助函数,该函数根据检查boost::python::object
参数手动处理调度。
这是一个完整的最小示例,演示通过索引或切片访问模型Counter
类的数据:
#include <vector>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>
#include <boost/python.hpp>
#include <boost/python/slice.hpp>
/// @brief Mockup class that creates a range from 0 to N.
struct counter
{
counter(std::size_t n)
{
data.reserve(n);
boost::copy(boost::irange(std::size_t(0), n), std::back_inserter(data));
}
std::vector<int> data;
};
/// @brief Handle index access for counter object.
int spam_getitem_index(const counter& self, int index)
{
// Attempt to convert to positive index.
if (index < 0)
{
index += self.data.size();
}
// Check for valid range.
if (index < 0 || self.data.size() <= index)
{
throw std::out_of_range("counter index out of range");
}
return self.data[index];
}
/// @brief Handle slicing for counter object.
boost::python::list spam_getitem_slice(
const counter& self,
boost::python::slice slice)
{
namespace python = boost::python;
python::list result;
// Boost.Python will throw std::invalid_argument if the range would be
// empty.
python::slice::range<std::vector<int>::const_iterator> range;
try
{
range = slice.get_indices(self.data.begin(), self.data.end());
}
catch (std::invalid_argument)
{
return result;
}
// Iterate over fully-closed range.
for (; range.start != range.stop; std::advance(range.start, range.step))
{
result.append(*range.start);
}
result.append(*range.start); // Handle last item.
return result;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<counter>("Counter", python::init<int>())
.def("__getitem__", &spam_getitem_slice)
.def("__getitem__", &spam_getitem_index)
;
}
交互式使用:
>>> from example import Counter
>>> counter = Counter(5)
>>> assert(counter[:] == [0,1,2,3,4])
>>> assert(counter[:-2] == [0,1,2])
>>> assert(counter[-2:] == [3,4])
>>> assert(counter[::2] == [0,2,4])
>>> assert(counter[1::2] == [1,3])
>>> assert(counter[100:] == [])
>>> assert(counter[1] == 1)
>>> assert(counter[-1] == 4)
>>> counter[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: counter index out of range
>>> counter[-100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: counter index out of range
注意当spam_getitem_index()
抛出std::out_of_range
异常时,Boost.Python会将其转换为相关的IndexError
Python异常。