使用Cython扩展模块包装std :: vector - 如何编写__setitem __()方法?

时间:2016-01-27 00:03:48

标签: cython

这似乎是一个应该有明显答案的问题,但出于某种原因,我无法在线找到任何示例。

我使用Cython在Python类中包装C ++对象的向量。我还有一个已编码的C ++类的Cython包装器。我可以使用__len__()__getitem__()resize()等多种方法正常工作,但__setitem__()方法给了我一些问题。

为简单起见,我使用int s的向量编码了一个小例子。我想如果我可以使这个代码工作,那么我可以构建它以获得我的C ++类的解决方案。

MyPyModule.pyx

# distutils: language = c++

from libcpp.vector cimport vector
from cython.operator cimport dereference as deref

cdef class MyArray:
    cdef vector[int]* thisptr
    def __cinit__(self):
        self.thisptr = new vector[int]()

    def __dealloc__(self):
        del self.thisptr

    def __len__(self):
        return self.thisptr.size()

    def __getitem__(self, size_t key):
        return self.thisptr.at(key)

    def resize(self, size_t newsize):
        self.thisptr.resize(newsize)

    def __setitem__(self, size_t key, int value):
        # Attempt 1:
        # self.thisptr.at(key) = value

        # Attempt 2:
        # cdef int* itemptr = &(self.thisptr.at(key))
        # itemptr[0] = value

        # Attempt 3:
        # (self.thisptr)[key] = value

        # Attempt 4:
        self[key] = value

当我尝试使用Attempt 1进行cythonize时,我收到了错误Cannot assign to or delete this。当我尝试尝试2时,创建了.cpp文件,但编译器抱怨:

error: cannot convert ‘__Pyx_FakeReference<int>*’ to ‘int*’ in assignment
   __pyx_v_itemptr = (&__pyx_t_1);

在尝试3时,Cython不会构建文件,因为Cannot assign type 'int' to 'vector[int]'。 (当我用C ++对象而不是int尝试这种风格时,它抱怨因为我有一个引用作为左值。)尝试4编译,但当我尝试使用它时,我得到一个段错误。 / p>

Cython docs表示不支持将引用作为左值返回,这很好 - 但是如何绕过它以便我可以为其中一个向量元素赋值?

3 个答案:

答案 0 :(得分:2)

通过指针访问向量有两种方法,

def __setitem__(self, size_t key, int value):
    deref(self.thisptr)[key] = value
    # or
    # self.thisptr[0][key] = value

Cython将这两种情况翻译如下:

Python: deref(self.thisptr)[key] = value
C++:    ((*__pyx_v_self->thisptr)[__pyx_v_key]) = __pyx_v_value;

Python: self.thisptr[0][key] = value
C++:    ((__pyx_v_self->thisptr[0])[__pyx_v_key]) = __pyx_v_value;

是等效的,即访问相同的矢量对象。

答案 1 :(得分:1)

您可以让Cython自己为您执行此操作,而不是尝试处理来自Cython代码的指针:

cdef class MyArray:
    cdef vector[int] thisptr

    def __len__(self):
        return self.thisptr.size()

    def __getitem__(self, size_t key):
        return self.thisptr[key]

    def __setitem__(self, size_t key, int value):
        self.thisptr[key] = value

    def resize(self, size_t newsize):
        self.thisptr.resize(newsize)

这种方法有问题吗?

答案 2 :(得分:0)

我已经接受了J.J. Hakala的回答(非常感谢!)。我调整了该方法以包含越界检查,因为它使用[]运算符而不是at()方法:

cdef class MyArray:
    (....)
    def __setitem__(self, size_t key, int value):
        if key < self.thisptr.size():
            deref(self.thisptr)[key] = value
        else:
            raise IndexError("Index is out of range.")