在我先前问题的this答案的基础上,我想制作一个memoryviews数组。
问题1
用固定的长度构建2D内存视图数组,例如
mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cdef const unsigned char[:,:] tmv = (mv1, mv2)
有了这个我得到:
TypeError: a bytes-like object is required, not 'tuple'
我尝试使用C指针数组:
ctypedef const unsigned char[:] k_t
cdef unsigned char* mva[2]
mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cdef k_t mvk1 = mv1
cdef k_t mvk2 = mv2
mva = (&mvk1, &mvk2)
但这也不起作用:
Cannot take address of memoryview slice
问题2
构建一个任意长的3D数组,基本上是上述2D数组对象的列表。 This其他类似问题的答案和Cython docs有关分配内存的信息使我更加接近(我相信我应该使用
malloc
和指针,如果不需要的话,我不想介绍C ++),但我仍然卡在问题#1。欢迎任何建议!编辑(问题1):即使在混合中抛出Cython数组也会给我同样的错误:
from cython cimport view
mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cvarr = view.array(shape=(2,1), itemsize=sizeof(char), format='B')
cvarr = (mv1, mv2)
print(cvarr[0][1])
# So far so good... this prints `50` as expected.
cdef const unsigned char[:,:] cvw = cvarr
# Adding this last line throws `a bytes-like object is required, not 'tuple'`
现在我真的很困惑。为什么元组对Cython数组有益,但对memview不利?
最佳答案
注意:甚至还没有一个完整的解决方案(至少目前是这样!)
我同意@DavidW的观点,如果一个连续的cython类型的memoryview拥有所有数据,并且将数据从python memoryview复制到其中,则可能会更好。这是正确的,特别是如果您计划只创建一次大型cython类型的memoryview,但计划对其进行多次迭代。
但是,您可以使用PyMemoryView_GET_BUFFER
获取属于该memoryview的基础缓冲区,从而获得指向python memoryview内容的指针。然后,您可以将数据memcpy
放入更大的数据结构中(以加快复制速度),也可以仅跟踪指针数组,每个元素都指向memoryview的数据(在迭代过程中速度较慢,因为您可能会从memoryview缓冲区指针跳转到memoryview缓冲区指针)。
这是一种获取指向python memoryview对象的基础数据的指针的方法。在cython github的cpython folder中,没有提到PyMemoryView
,因此我不得不手动包装它:
from cpython.object cimport PyObject
cdef extern from "Python.h":
Py_buffer* PyMemoryView_GET_BUFFER(PyObject *mview)
cdef object mv1 = memoryview(b'1234')
cdef Py_buffer* buf = PyMemoryView_GET_BUFFER(<PyObject*>mv1)
cdef char* buf_ptr = <char*>buf.buf
print(buf_ptr)#prints b'1234'
更新1:
无法100%确定3D阵列结构应该是什么样子,所以我只是考虑2D情况。既然您说过不想引入C ++,我创建了这种
array_t
数据类型,其行为类似于矢量(很好,是指向一堆void*
的指针)。很多丑陋的样板,但是在这里:from cpython.object cimport PyObject
from libc.stdlib cimport malloc, calloc, realloc, free
from libc.string cimport memcpy, memmove
cdef extern from "Python.h":
Py_buffer* PyMemoryView_GET_BUFFER(PyObject *mview)
cdef char* get_view_ptr(object view):
cdef Py_buffer* py_buf = PyMemoryView_GET_BUFFER(<PyObject*>view)
cdef char* ptr = <char*>py_buf.buf
return ptr
ctypedef struct array_t:
void** data
int max_items
int num_items
cdef void array_init(array_t* array):
array.data = NULL
array.max_items = 0
array.num_items = 0
cdef void array_add(array_t* array, void* item):
if array.max_items == 0:
array.max_items = 10
array.num_items = 0
array.data = <void**>calloc(array.max_items, sizeof(void*))
if array.max_items == array.num_items:
array.max_items *= 2
array.data = <void**>realloc(array.data, array.max_items * sizeof(void*))
array.data[array.num_items] = item
array.num_items += 1
cdef void array_set(array_t* array, int index, void *item):
if index < 0 or index >= array.max_items:
return
array.data[index] = item
cdef void* array_get(array_t* array, int index):
if index < 0 or index >= array.max_items:
return NULL
return array.data[index]
cdef void array_remove(array_t* array, int index):
cdef:
void* src
void* dest
if index < 0 or index >= array.max_items:
return
array.data[index] = NULL
if index+1 != array.max_items:
src = &array.data[index+1]
dest = &array.data[index]
memmove(dest, src, (array.max_items - index) * sizeof(void*))
array.num_items -= 1
cdef void array_free(array_t* array):
free(array.data)
cdef int i
cdef array_t a
cdef object mv1 = memoryview(b'12345')
cdef object mv2 = memoryview(b'67890')
cdef object mv3 = memoryview(b'abcde')
cdef object mv4 = memoryview(b'!@#$%')
array_init(&a)
array_add(&a, get_view_ptr(mv1))
array_add(&a, get_view_ptr(mv2))
array_add(&a, get_view_ptr(mv3))
array_add(&a, get_view_ptr(mv4))
for i in range(a.num_items):
print(i, <char*>array_get(&a, i))