假设C ++中有一个类MyArray
。它实现了一个SomeType
数组为了在Python中为它创建一个__getitem__
函数,我做了类似的事情
const SomeType& getitem(const MyArray *arr, PyObject *slice) {
// ???
}
BOOST_PYTHON_MODULE(mymodule)
{
class_<MyArray>("MyArray")
.def("__getitem__", &getitem)
// probably some other methods...
;
}
可以使用these functions在slice
中获取索引。但是,“Boost::Python is designed with the idea in mind that users never touch a PyObject*”。
这样做有更好的“提升方式”吗?
答案 0 :(得分:3)
Boost.Python旨在最大限度地减少与PyObject
交互的需要,它通常通过以下方式实现:
boost::python::object
。例如,可以通过C ++以类似于Python的方式访问Python对象的接口。以下演示了如何访问引用Python start
实例的boost::python::object
的{{1}}属性:
slice
虽然这种方法有效,但它往往会产生很多样板代码:在提供namespace python = boost::python;
python::object slice = get_slice_object();
python::object start = slice.attr("start");
std::size_t start_index = !start.is_none()
? python::extract<std::size_t>(start) // Extract index.
: 0; // Default.
时创建默认值,处理零长度切片,以及将负索引转换为正索引。在这种情况下,Boost.Python提供了一个更高级别的类型包装器boost::python::slice
,它具有None
成员函数,可以删除大部分样板代码。这是一个完整的最小例子:
get_indices()
交互式使用:
#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 slicing for counter object.
boost::python::list spam_getitem(
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)
;
}