使用 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])
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef int[:] ca = 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
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef int[:] ca = a
cdef int overhead(object a):
cdef int[:] ca = a
return ca[0]
cdef int no_overhead(int[:] ca):
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))
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
# access underlying pointer:
print(a.data.as_ints[0])
from libc.string cimport memset
memset(a.data.as_voidptr, 0, len(a) * sizeof(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 cpython cimport array
import array
cdef array.array int_array_template = array.array('i', [])
cdef array.array newarray
# 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))
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef array.array b = 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
cdef int resize(array.array self, Py_ssize_t n) except -1
快速调整大小/重新分配。不适合重复的小增量;将底层数组调整为正好所需的量。
@cython.cfunc
@cython.exceptval(-1)
def resize_smart(self: array.array, n: cython.Py_ssize_t) -> cython.int
cdef int resize_smart(array.array self, Py_ssize_t n) except -1
对于小增量效率很高;使用增长模式,提供摊销线性时间追加。
@cython.cfunc
@cython.inline
def clone(template: array.array, length: cython.Py_ssize_t, zero: cython.bint) -> array.array
cdef inline array.array clone(array.array template, Py_ssize_t length, bint zero)
快速创建新数组,给定模板数组。类型将与template
相同。如果zero
为True
,则新数组将用零初始化。
@cython.cfunc
@cython.inline
def copy(self: array.array) -> array.array
cdef inline array.array copy(array.array self)
复制数组。
@cython.cfunc
@cython.inline
@cython.exceptval(-1)
def extend_buffer(self: array.array, stuff: cython.p_char, n: cython.Py_ssize_t) -> cython.int
cdef inline int extend_buffer(array.array self, char* stuff, Py_ssize_t n) except -1
高效地追加相同类型的新数据(例如,相同数组类型)n
:元素数量(不是字节数!)。
@cython.cfunc
@cython.inline
@cython.exceptval(-1)
def extend(self: array.array, other: array.array) -> cython.int
cdef inline int extend(array.array self, array.array other) except -1
使用来自另一个数组的数据扩展数组;类型必须匹配。
@cython.cfunc
@cython.inline
def zero(self: array.array) -> cython.void
cdef inline void zero(array.array self)
将数组的所有元素设置为零。