使用 Python 数组

注意

此页面使用两种不同的语法变体

  • Cython 特定的 cdef 语法,旨在使类型声明简洁,并易于从 C/C++ 的角度阅读。

  • 纯 Python 语法,允许在 纯 Python 代码 中使用静态 Cython 类型声明,遵循 PEP-484 类型提示和 PEP 526 变量注释。

    要在 Python 语法中使用 C 数据类型,您需要在要编译的 Python 模块中导入特殊的 cython 模块,例如:

    import cython
    

    如果您使用纯 Python 语法,我们强烈建议您使用最新的 Cython 3 版本,因为与 0.29.x 版本相比,这里已经进行了重大改进。

Python 具有内置的数组模块,支持基本类型的动态一维数组。可以从 Cython 内部访问 Python 数组的底层 C 数组。同时,它们是普通的 Python 对象,可以存储在列表中,并在使用 multiprocessing 时在进程之间序列化。

与使用 malloc()free() 的手动方法相比,这提供了 Python 的安全和自动内存管理,与 Numpy 数组相比,不需要安装依赖项,因为 array 模块内置于 Python 和 Cython 中。

使用内存视图的安全用法

from cython.cimports.cpython import array
import array
a = cython.declare(array.array, array.array('i', [1, 2, 3]))
ca = cython.declare(cython.int[:], a)

print(ca[0])

注意:导入将常规 Python 数组对象引入命名空间,而 cimport 添加了从 Cython 可访问的函数。

Python 数组使用类型签名和初始值的序列来构造。有关可能的类型签名,请参阅 Python 文档中的 array 模块

请注意,当 Python 数组被分配给类型为内存视图的变量时,将会有轻微的开销来构造内存视图。但是,从那时起,该变量可以无开销地传递给其他函数,只要它被类型化

from cython.cimports.cpython import array
import array

a = cython.declare(array.array, array.array('i', [1, 2, 3]))
ca = cython.declare(cython.int[:], a)

@cython.cfunc
def overhead(a: cython.object) -> cython.int:
    ca: cython.int[:] = a
    return ca[0]

@cython.cfunc
def no_overhead(ca: cython.int[:]) -> cython.int:
    return ca[0]

print(overhead(a))  # new memory view will be constructed, overhead
print(no_overhead(ca))  # ca is already a memory view, so no overhead

对原始 C 指针的零开销、不安全访问

为了避免任何开销并能够将 C 指针传递给其他函数,可以将底层连续数组作为指针访问。没有类型或边界检查,因此请注意使用正确的类型和符号。

from cython.cimports.cpython import array
import array

a = cython.declare(array.array, array.array('i', [1, 2, 3]))

# access underlying pointer:
print(a.data.as_ints[0])

from cython.cimports.libc.string import memset

memset(a.data.as_voidptr, 0, len(a) * cython.sizeof(cython.int))

请注意,对数组对象的任何更改长度的操作都可能使指针失效。

克隆、扩展数组

为了避免必须使用 Python 模块中的数组构造函数,可以创建一个与模板类型相同的数组,并预先分配给定数量的元素。如果需要,数组将初始化为零。

from cython.cimports.cpython import array
import array

int_array_template = cython.declare(array.array, array.array('i', []))
cython.declare(newarray=array.array)

# create an array with 3 elements with same type as template
newarray = array.clone(int_array_template, 3, zero=False)

数组也可以扩展和调整大小;这避免了重复的内存重新分配,这将在逐个追加或删除元素时发生。

from cython.cimports.cpython import array
import array

a = cython.declare(array.array, array.array('i', [1, 2, 3]))
b = cython.declare(array.array, array.array('i', [4, 5, 6]))

# extend a with b, resize as needed
array.extend(a, b)
# resize a, leaving just original three elements
array.resize(a, len(a) - len(b))

API 参考

数据字段

data.as_voidptr
data.as_chars
data.as_schars
data.as_uchars
data.as_shorts
data.as_ushorts
data.as_ints
data.as_uints
data.as_longs
data.as_ulongs
data.as_longlongs  # requires Python >=3
data.as_ulonglongs  # requires Python >=3
data.as_floats
data.as_doubles
data.as_pyunicodes

直接访问底层连续的 C 数组,使用给定的类型;例如,myarray.data.as_ints

函数

以下函数可从数组模块中访问 Cython

@cython.cfunc
@cython.exceptval(-1)
def resize(self: array.array, n: cython.Py_ssize_t) -> cython.int

快速调整大小/重新分配。不适合重复的小增量;将底层数组调整为正好所需的量。


@cython.cfunc
@cython.exceptval(-1)
def resize_smart(self: array.array, n: cython.Py_ssize_t) -> cython.int

对于小增量效率很高;使用增长模式,提供摊销线性时间追加。


@cython.cfunc
@cython.inline
def clone(template: array.array, length: cython.Py_ssize_t, zero: cython.bint) -> array.array

快速创建新数组,给定模板数组。类型将与template相同。如果zeroTrue,则新数组将用零初始化。


@cython.cfunc
@cython.inline
def copy(self: array.array) -> array.array

复制数组。


@cython.cfunc
@cython.inline
@cython.exceptval(-1)
def extend_buffer(self: array.array, stuff: cython.p_char, n: cython.Py_ssize_t) -> cython.int

高效地追加相同类型的新数据(例如,相同数组类型)n:元素数量(不是字节数!)。


@cython.cfunc
@cython.inline
@cython.exceptval(-1)
def extend(self: array.array, other: array.array) -> cython.int

使用来自另一个数组的数据扩展数组;类型必须匹配。


@cython.cfunc
@cython.inline
def zero(self: array.array) -> cython.void

将数组的所有元素设置为零。