Python Cython中的MemoryView数组

Python Cython中的MemoryView数组,python,arrays,cython,memoryview,typed-memory-views,Python,Arrays,Cython,Memoryview,Typed Memory Views,基于我之前问题的答案,我想创建一系列MemoryView 问题1 构建具有固定长度的二维MemoryView数组,例如 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 c

基于我之前问题的答案,我想创建一系列MemoryView

问题1 构建具有固定长度的二维MemoryView数组,例如

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阵列对象的列表。其他类似问题的答案和分配内存使我有点接近(我相信我应该使用<代码> MalOC/和指针,如果不需要,我不想引入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没有好处?

这似乎解决了问题#1:

然而,它在第4行发布了两条关于

Obtaining 'unsigned char [4]' from externally modifiable global Python value

我想我可以忽略这些警告,因为我实际使用的是
cvw
,这是一个常量

注意:甚至还没有接近一个完整的解决方案(至少目前如此!)

我同意@DavidW的观点,即如果一个连续的cython类型的memoryview拥有所有数据,并且数据从python memoryview复制到它中,可能会更好。这是正确的,尤其是如果您计划只创建一次巨型cython类型的memoryview,但计划多次迭代它

但是,通过使用
PyMemoryView\u get\u BUFFER
获取属于该memoryview的底层缓冲区,可以获得指向python memoryview内容的指针。然后,您可以
memcpy
将数据放入一个更大的数据结构中(以便更快地复制),或者只跟踪一个指针数组,每个元素都指向memoryview的数据(在迭代过程中会更慢,因为您会在内存中从memoryview缓冲区指针跳到memoryview缓冲区指针)

下面是一种获取python memoryview对象底层数据指针的方法。在cython github中,没有提到
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'

如果这是胡说八道的话,请提前道歉,但是像
cdef unsigned char[::view.indirect_contracting,:]tmv=(mv1,mv2)
这样的东西可能对您有意义?它至少可以无误地进行循环。@PaulPanzer编译,但在运行时抛出一个错误:
需要一个类似字节的对象,而不是“元组”
您试图创建的2D memoryview是单个内存区域(解释为2D数组)上的视图。由于您试图组合多个独立的内存区域,因此它肯定不会起作用。我想不出任何内置的Cython结构是合适的。您可能可以从Paul Panzer建议的间接数组中生成一些内容,但这需要大量包装代码,而且我不知道该怎么做。@user3758232您的MemoryView是否都是从单个大数组中提取的5长序列?如果是这样的话,你最好只存储一个偏移量数组,而不是MemoryView1)我说的偏移量是指“从数组开始的元素数”。不过,您也可以使用指针。2) Cython MemoryView在其实现中有指针,但更复杂——例如,它们可以跟踪形状(对于2D数组),并处理Python引用计数。3) 听起来您想要的数据结构在Cython中并不直接存在(而且可能不容易构建)。我建议要么复制数据,要么将MemoryView放在Python列表中(可能没有您想象的那么慢)
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'
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))