美文网首页工作笔记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