2014-10-04 61 views
0

假設在C++中有一個類。它實現的SomeType數組爲了使__getitem__功能,它在Python,我做這樣的事情如何處理boost :: python中的Python切片對象?

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*」。

有沒有更好的「提升方式」來做到這一點?

回答

3

Boost.Python的設計,以儘量減少需要與PyObject互動,同時也經常完成此:

  • 提供了更高級別的類型包裝。
  • 允許通過關聯的boost::python::object訪問Python對象的接口。

例如,可以通過C++以類似於Python中的方式訪問Python對象的接口。下面演示訪問start屬性boost::python::object的指代一個Python 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. 

雖然這種方法的工作原理,它趨向於導致多樣板代碼:當提供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:] == []) 
+0

很好的例子!這可以擴展來處理切片和索引(例如,計數器[2:-1]和計數器[2])? – Felix 2017-02-22 15:29:42