首页 > 解决方案 > 无符号字符的 Cython 数组可以包含零吗?

问题描述

我想通过使用 Cython 来加速 Python 项目中的一些核心例程(我对两者都很陌生)。我正在编写带有相应 .py 类型信息的扩充 .pxd 文件。在一个 .py 中,我有一个类,其中有一个实例变量array.array,我想成为 Cython 中的无符号字符数组。它可以编译,但我发现(经过艰苦的调试)每当在数组中的某处写入 0 时,它的长度就会发生变化,并且如果在 0 元素之外访问数组,则会引发 IndexError。

这是一个小(不确定它是最小的)示例。

内存.py:

import array

class Ram:

def __init__(self):
    self.ram = array.array('B', [1,1,0,1,1])
    print(len(self.ram))

内存.pxd:

cdef class Ram:
    cdef unsigned char[5] ram

编译成扩展模块后得到的结果:

>>> import ram
>>> ram.Ram()
2

我尝试使用编译器指令关闭绑定检查,boundscheck = False但无济于事。

如果我在 ram.pxd 中使用,它会按预期工作(长度为 5)cdef unsigned int[5] ram,但我想使用字节数组。

如何保持数组的长度固定,同时仍然可以在其中写入 0?

(我正在使用 Cython 0.29.13 和 Python 3.7.4)

标签: pythoncython

解决方案


您的问题在于len数组而不是数组。len是一个 Python 函数,因此不是为 char 数组“真正”定义的。然而,Cython 试图提供答案并默认使用strlen计数方法,直到第一个 0 字节。在这种情况下,这是错误的,但这是一个明智的一般最佳猜测。

您可以定义这样的数组,并存储任何数据,包括0. 你不能依赖 Cythonlen来获取长度——在这种情况下,长度是一个常数,所以你知道它,但如果它是一个动态分配的数组,你将负责存储它。您可能还必须小心 Cython 的自动转换为 Python 字符串。


编辑:更多细节,因为我认为你并没有完全按照你的想法做:

cdef const char[5] ram

定义了一个长度为 5 的 C 数组。这是非常节省空间的(除了 5 个字符之外它不存储任何额外的数据),在 Cython 中可以快速访问,但没有 Python 等效项,因此在 Python 中访问它需要转换(自动,或者你自己做的事情)

ram = array.array(...)

将 Python 数组复制到 C 数组中。

我怀疑您应该改用memoryview:

cdef unsigned char[::1] ram # ::1 specifies C contiguous

这些空间效率稍低(它们存储一些 Python 引用计数信息和形状),并且在 Cython 中访问速度中等(您可以通过关闭边界检查和负索引以及指定 C 来使它们更快正如我在这里所做的那样)。更重要的是,它们可以在 Python 中访问(尽管以常规 Python 速度),并且 Cython 不会尝试将它们视为 C 字符串。

ram = array.array(...)

创建包含在数组中的数据的视图(无副本 - 它共享数据)。


推荐阅读