【问题标题】:How to deal with Python slice objects in boost::python?如何处理 boost::python 中的 Python 切片对象?
【发布时间】:2014-10-04 17:46:03
【问题描述】:

假设在 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*”。

有没有更好的“提升方式”来做到这一点?

【问题讨论】:

    标签: python c++ python-3.x slice boost-python


    【解决方案1】:

    Boost.Python 旨在最大限度地减少与PyObject 交互的需要,它通常通过以下方式实现:

    • 提供更高级别的类型包装器。
    • 允许通过关联的boost::python::object 访问 Python 对象的接口。

    例如,可以通过 C++ 访问 Python 对象的接口,其方式与在 Python 中的访问方式类似。下面演示了访问引用 Python slice 实例的 boost::python::objectstart 属性:

    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.
    

    虽然这种方法有效,但它往往会导致大量样板代码:在提供 None 时创建默认值、处理零长度切片以及将负索引转换为正索引。在这种情况下,Boost.Python 提供了一个更高级别的类型包装器 boost::python::slice,它具有一个 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)
        ;
    }
    

    互动使用:

    >>> 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:] == [])
    

    【讨论】:

    • 很好的例子!这可以扩展到同时处理切片和索引(例如,counter[2:-1] 和 counter[2])吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 2015-03-27
    • 1970-01-01
    • 2011-04-24
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多