美文网首页工作笔记Python
Cython:解决BufferError: Object is

Cython:解决BufferError: Object is

作者: txfly | 来源:发表于2020-06-09 15:11 被阅读0次

在使用Cython时,碰到了BufferError: Object is not writable.错误,下面介绍解决办法。

BufferError: Object is not writable.错误主要是将bytes以memoryview方式传递给函数时引起的,例如:

cpdef unsigned char checksum(unsigned char[:] data) nogil:
    cdef int i
    cdef int length = data.shape[0] - 1
    cdef int ret = 0
    for i in range(0, length):
        ret += data[i]
    return ret % 256

该函数可以接受bytearray或者array类型参数,但使用bytes类型时会报错:

File "demo.pyx", line 1, in demo.checksum
    cpdef unsigned char checksum(unsigned char[:] data) nogil:
  File "stringsource", line 658, in View.MemoryView.memoryview_cwrapper
  File "stringsource", line 349, in View.MemoryView.memoryview.__cinit__
BufferError: Object is not writable.

主要原因是bytes类型是不可变的,转换为memoryview后变为只读,而Cython生成的代码中会检查memoryview是否可写,否则就报错。下面是Cython生成的c代码:

static PyObject *__pyx_pw_5utils_7checksum(PyObject *__pyx_self, PyObject *__pyx_arg_data) {
  __Pyx_memviewslice __pyx_v_data = { 0, 0, { 0 }, { 0 }, { 0 } };
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("checksum (wrapper)", 0);
  assert(__pyx_arg_data); {
   // 检查buffer是否可写
    __pyx_v_data = __Pyx_PyObject_to_MemoryviewSlice_ds_unsigned_char(__pyx_arg_data, PyBUF_WRITABLE); if (unlikely(!__pyx_v_data.memview)) __PYX_ERR(0, 1, __pyx_L3_error)
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L3_error:;
  __Pyx_AddTraceback("demo.checksum", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_5utils_6checksum(__pyx_self, __pyx_v_data);

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

其中__pyx_v_data = __Pyx_PyObject_to_MemoryviewSlice_ds_unsigned_char(__pyx_arg_data, PyBUF_WRITABLE); if (unlikely(!__pyx_v_data.memview)) __PYX_ERR(0, 1, __pyx_L3_error)就是关键所在,它会检查转换为memoryview后缓冲区是否可写。

解决办法有两种,一是用const修饰memoryview,允许只读缓冲区作为输入,修改后正确代码如下:

cpdef unsigned char checksum(const unsigned char[:] data) nogil:
    cdef int i
    cdef int length = data.shape[0] - 1
    cdef int ret = 0
    for i in range(0, length):
        ret += data[i]
    return ret % 256

该函数接受bytes、bytearray和array作为输入。

二是直接以bytes数组作为输入,并指定数组长度:

cpdef unsigned char checksum1(unsigned char[] data, int length) nogil:
    cdef int i
    cdef int ret = 0
    for i in range(0, length - 1):
        ret += data[i]
    return ret % 256

版权声明:本文为「txfly」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://www.jianshu.com/p/fa386e537fe9

相关文章

网友评论

    本文标题:Cython:解决BufferError: Object is

    本文链接:https://www.haomeiwen.com/subject/lopstktx.html