如何使用Cython类型化内存视图从Python接受字符串?

14

如何编写一个Cython函数,以typed memoryview的形式接受一个字节串对象(普通字符串、bytearray或遵循buffer protocol的其他对象)?

根据Unicode and Passing Strings Cython教程页面的说明,以下代码应该可以实现:

cpdef object printbuf(unsigned char[:] buf):
    chars = [chr(x) for x in buf]
    print repr(''.join(chars))

它适用于字节数组和其他可写缓冲区:

$ python -c 'import test; test.printbuf(bytearray("test\0ing"))'
'test\x00ing'

但是对于普通字符串和其他只读缓冲区对象,它不起作用:
$ python -c 'import test; test.printbuf("test\0ing")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "test.pyx", line 1, in test.printbuf (test.c:1417)
  File "stringsource", line 614, in View.MemoryView.memoryview_cwrapper (test.c:6795)
  File "stringsource", line 321, in View.MemoryView.memoryview.__cinit__ (test.c:3341)
BufferError: Object is not writable.

看生成的C代码,Cython总是向PyObject_GetBuffer()传递PyBUF_WRITABLE标志,这解释了异常。
我可以手动获取一个视图到缓冲对象中,但这不太方便。
from cpython.buffer cimport \
    PyBUF_SIMPLE, PyBUF_WRITABLE, \
    PyObject_CheckBuffer, PyObject_GetBuffer, PyBuffer_Release

cpdef object printbuf(object buf):
    if not PyObject_CheckBuffer(buf):
        raise TypeError("argument must follow the buffer protocol")
    cdef Py_buffer view
    PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE)
    try:
        chars = [chr((<unsigned char *>view.buf)[i])
                 for i in range(view.len)]
        print repr(''.join(chars))
    finally:
        PyBuffer_Release(&view)

$ python -c 'import test; test.printbuf(bytearray("test\0ing"))'
'test\x00ing'
$ python -c 'import test; test.printbuf("test\0ing")'
'test\x00ing'

我是否做错了什么,或者Cython不支持将只读缓冲区对象(如普通字符串)强制转换为类型化的memoryview对象?

我在这里找到了你的补丁(https://mail.python.org/pipermail/cython-devel/2015-February/004316.html),即使添加“const”,也没有帮助,这意味着建议的文档不起作用。 - dashesy
const现在可以在Cython 0.28.4中正常工作了。 - winni2k
2个回答

21

尽管文档表明如此,但是Cython(至少到版本0.22)并支持将只读缓冲区对象强制转换为类型化的memoryview对象。即使没有写入权限,Cython始终向PyObject_GetBuffer()传递PyBUF_WRITABLE标志。这会导致只读缓冲区对象引发异常。

在Cython开发者邮件列表上提出了这个问题, 甚至包括了一个(非常粗糙的)补丁。我从未收到回复,因此我认为Cython开发人员对修复此错误不感兴趣。


我也遇到了同样的问题,为什么他们不感兴趣修复这个问题呢? - avocado
你可以尝试将补丁提交为 GitHub pull 请求,并添加一些测试用例。 - joeln
你尝试过通过GitHub推送补丁吗? - K3---rnc
重复其他人所说的,这很可能意味着他们没有看到或理解补丁,或者没有能力处理其粗糙状态——并不是他们不想修复错误。当有能力这样做时,回复是正常的人类行为,即使对于你不感兴趣的事情也是如此。 - fuzzyTew

10
这个问题在Cython 0.28中得到了修复,发布于2018-03-13 (PR #1869)。 changelog 中指出:

const修饰符可以应用于memoryview声明,以允许只读缓冲区作为输入。

文档中还有一个新的章节

如果您这样编写函数,您给出的示例将在Cython 0.28中运行:

cpdef object printbuf(const unsigned char[:] buf):
    chars = [chr(x) for x in buf]
    print repr(''.join(chars))

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