Python列表中的额外元素

Nic*_*lfi 8 python cython

使用Cython,我试图将Python列表转换为Cython数组,反之亦然.Python列表包含0到255范围内的数字,因此我将数组的类型指定为unsigned char数组.这是我的代码来进行转换:

from libc.stdlib cimport malloc

cdef to_array(list pylist):
    cdef unsigned char *array 
    array = <unsigned char *>malloc(len(pylist) * sizeof(unsigned char))
    cdef long count = 0

    for item in pylist:
        array[count] = item
        count += 1
    return array

cdef to_list(array):
    pylist = [item for item in array]
    return pylist

def donothing(pylist):
    return to_list(to_array(pylist))
Run Code Online (Sandbox Code Playgroud)

问题在于,在Cython数组中生成了多条垃圾数据,并且当转换为Python列表时,垃圾数据会继续存在.例如,donothing应该什么都不做,并将python列表返回给我,不变.这个函数只是用于测试转换,但是当我运行它时,我得到类似的东西:

In[56]:  donothing([2,3,4,5])
Out[56]: [2, 3, 4, 5, 128, 28, 184, 6, 161, 148, 185, 69, 106, 101]
Run Code Online (Sandbox Code Playgroud)

这些数据来自代码中的哪里,以及如何清理这些垃圾以免浪费内存?

PS可能有更好的从Python列表中获取数字并将它们注入unsigned char数组的版本.如果是这样,请完全指导我一个更好的方法.

Vee*_*rac 3

to_array有一个无类型的返回值。此外,您将结果分配给一个无类型值。因此,Cython 被迫转换char *为 Python 类型。

Cython 转换为bytes,因为char大约为bytes。不幸的是,如果没有明确给出的长度,Cython 会假设 是以char *null 终止的。这就是导致问题的原因:

convert_lists.donothing([1, 2, 3, 0, 4, 5, 6])
#>>> [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

当没有零时,Cython 将直接读取,直到找到 1,超过实际分配的内存。

您实际上无法for x in my_pointer_arrray对任意 Cython 类型执行此操作。该for循环实际上对错误转换的 进行操作bytes

您可以通过键入将保存数组的所有char、显式传递长度并循环范围(键入循环变量时这也会更快)或使用某种包装器来解决此问题。有关使用哪些包装器数组的想法,此问答对已为您介绍


另请注意,使用手动分配时应非常小心错误。malloc数据不会被垃圾收集,因此如果您在代码路径中出错,您将泄漏内存。您应该检查如何处理每个具体情况。