在使用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
网友评论