python - Cython 中的内存 View 数组

标签 python arrays cython memoryview typed-memory-views

建立在this之上回答我之前的问题,我想制作内存 View 数组。

问题 1

构建具有固定长度的内存 View 的二维数组,例如

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类型的内存 View 拥有所有数据并且数据从Python内存 View 复制到其中,可能会更好。尤其是如果您计划只创建一次巨大的 cython 类型的内存 View ,但计划对其进行多次迭代,则尤其如此。

但是,您可以通过使用PyMemoryView_GET_BUFFER来获取指向Python内存 View 内容的指针,以获取属于该内存 View 的底层缓冲区。然后,您可以将数据 memcpy 存储到更大的数据结构中(以便更快地复制),或者只跟踪指针数组,每个元素都指向内存 View 的数据(在复制过程中速度较慢)迭代,因为您将在内存中从内存 View 缓冲区指针跳转到内存 View 缓冲区指针)。

这是一种获取指向Python内存 View 对象底层数据的指针的方法。来自 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))

关于python - Cython 中的内存 View 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50439908/

相关文章:

c++ - ctypes 类成员访问段错误

python - 如何获得字符串中每个字母的计数?

在另一个类中使用比较器对对象进行Java数组二进制搜索

python - Cython,纯 Python 模式 : . pxd 和 @locals

c++ - 使用 Cython 将功能公开给另一个应用程序

python - 同一包中的 cython 导入错误

python - 如何在 PyYaml 中添加自定义嵌套标签?

python - 模型和继承

javascript - 将数组元素分组为 n 个集合

java - Android RecyclerView 根据条件传递和显示 Item 数据