在字节对象上获取指向Python memoryview的指针

8
我是一名有用的助手,可以翻译文本。

我有一个指向bytes对象的Python memoryview,我想在Cython中对其进行一些处理。

我的问题是:

  • 因为bytes对象不可写,所以Cython不允许从中构建类型化(Cython)memoryview
  • 我也不能使用指针,因为我无法获取指向memoryview开头的指针

示例:

在Python中:

array = memoryview(b'abcdef')[3:]

在Cython中:
  • cdef char * my_ptr = &array[0] 编译失败,错误信息为:Cannot take address of Python variable
  • cdef char[:] my_view = array 运行时失败,错误信息为:BufferError: memoryview: underlying buffer is not writable

如何解决这个问题?


第一个问题:在你的Cython函数中如何声明array参数? - Pierre de Buyl
@PierredeBuyl 我将其作为Python对象传递。像这样:Cython: def myfunc(arr): pass - ARF
嗨,经过一些文档查找和谷歌搜索,如果你收到的只是一个memoryview,似乎很难获得读写访问权限。你应该提到如何在第一次创建memoryview时进行操作。如果你可以获得一个Py_buffer结构体,这可能会有所帮助。https://docs.python.org/3.5/c-api/buffer.html - Pierre de Buyl
@PierredeBuyl 非常感谢你提供 Py_buffer 结构的提示!我得出了相同的解决方案。请查看下面我对自己问题的回答... - ARF
4个回答

5

好的,在查阅 Python API 后,我找到了一个解决方案,可以在内存视图中获取指向 bytes 对象缓冲区的指针(这里称为 bytes_view = memoryview(bytes()))。也许这可以帮助其他人:

from cpython.buffer cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_ANY_CONTIGUOUS, PyBUF_SIMPLE


cdef Py_buffer buffer
cdef char * my_ptr

PyObject_GetBuffer(bytes, &buffer, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS)
try:
    my_ptr = <char *>buffer.buf
    # use my_ptr
finally:
    PyBuffer_Release(&buffer)

1
这正是我所寻找的...但你代码中的“buffer_view”是什么?你是指“buffer”吗? - Udai F.mHd
1
{btsdaf} - Conrad Parker
{btsdaf} - ARF
{btsdaf} - ARF

3
使用bytearray(根据@CheeseLover的回答)可能是做事情的正确方式。我的建议是完全使用bytearrays,从而避免临时转换。但是:char*可以直接从Python字符串(或bytes)创建-请参见链接部分的末尾:
cdef char * my_ptr = array
# you can then convert to a memoryview as normal in Cython
cdef char[:] mview = <char[:len(array)]>my_ptr

几条警告:

  1. 记住 bytes 是不可变的,如果您尝试修改该 memoryview,则可能会导致问题。
  2. my_ptr(因此也包括 mview)只在 array 有效的时间内有效,因此请确保在需要访问数据时保留对 array 的引用。

谢谢。我的问题是,我得到了一个“bytes”对象。因此,我不能在不产生实例化成本的情况下切换到“bytesarray”。可以从“bytes()”创建“char *”,但无法从“memoryview(bytes())”创建。你的建议失败了,“TypeError: expected bytes, memoryview found” - ARF
啊,抱歉——我有点误解了。你可以从内存视图中获取bytes(无需复制)使用array.obj(仅适用于Python >=3.3),然后将其转换为char* - DavidW
我也尝试过这种方法:它会丢失memoryview的偏移量。对于test = memoryview(b'abcdef')[3:]bytes(test) == b'def',而test.obj == b'abcdef'。到目前为止,我找到的唯一解决方案是下面我的答案中非常冗长的过程。-虽然我很想摆脱那些可怕的代码混乱。 - ARF

2
你可以使用bytearray创建可变的内存视图。请注意,这不会改变字符串,只会改变bytearray
data = bytearray('python')
view = memoryview(data)
view[0] = 'c'
print data
# cython

确实。这就是我现在正在做的事情。但是实例化一个临时的bytearray对象会破坏使用Cython加速算法的整个目的。 - ARF

1
如果您不想让cython的memoryview出现“底层缓冲区不可写”的错误,那么您就不应该请求可写的缓冲区。一旦您进入C域,您可以简单地处理这种可写性。所以这样做是有效的:
cdef const unsigned char[:] my_view = array
cdef char* my_ptr = <char*>&my_view[0]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接