使用 Cython 扩展模块包装 std::vector - 如何编写 __setitem__() 方法?
Using Cython extension module to wrap std::vector - How do I program __setitem__() method?
这似乎是一个应该有明显答案的问题,但出于某种原因我无法在网上找到任何示例。
我正在使用 Cython 在 Python class 中包装 C++ 对象的向量。我还有一个已编码的 C++ class 的 Cython 包装器。我可以使用 __len__()
、__getitem__()
和 resize()
等几种方法正常工作,但 __setitem__()
方法给我带来了问题。
为简单起见,我使用 int
s 的向量编写了一个小示例。我想如果我能让这段代码工作,那么我可以以此为基础为我的 C++ class 获得解决方案。
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
当我尝试使用尝试 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 编译,但是当我尝试使用它时,我得到了一个段错误。
Cython docs 说不支持将引用作为左值返回,这很好——但我该如何绕过它,以便我可以为我的向量之一分配一个新值元素?
您可以让 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)
这种做法有什么问题吗?
通过指针访问向量有两种方式,
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;
它们是等价的,即访问相同的矢量对象。
我已经接受了J.J。 Hakala 的回答(非常感谢!)。我调整了该方法以包含 out-of-bounds 检查,因为它使用 []
运算符而不是 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.")
这似乎是一个应该有明显答案的问题,但出于某种原因我无法在网上找到任何示例。
我正在使用 Cython 在 Python class 中包装 C++ 对象的向量。我还有一个已编码的 C++ class 的 Cython 包装器。我可以使用 __len__()
、__getitem__()
和 resize()
等几种方法正常工作,但 __setitem__()
方法给我带来了问题。
为简单起见,我使用 int
s 的向量编写了一个小示例。我想如果我能让这段代码工作,那么我可以以此为基础为我的 C++ class 获得解决方案。
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
当我尝试使用尝试 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 编译,但是当我尝试使用它时,我得到了一个段错误。
Cython docs 说不支持将引用作为左值返回,这很好——但我该如何绕过它,以便我可以为我的向量之一分配一个新值元素?
您可以让 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)
这种做法有什么问题吗?
通过指针访问向量有两种方式,
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;
它们是等价的,即访问相同的矢量对象。
我已经接受了J.J。 Hakala 的回答(非常感谢!)。我调整了该方法以包含 out-of-bounds 检查,因为它使用 []
运算符而不是 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.")