Skip to content

类型系统深入

本章深入讲解Cython类型系统。正确选择类型是获得C级性能的关键——类型决定编译器生成Python对象操作还是C代码。

学习路径:C类型 → Python对象类型 → 结构体/联合体 → 枚举/常量 → 类型别名

核心原则:

  • 数值计算用C类型(int、double)获得最高性能
  • 与Python互调用object类型(list、dict、str)
  • 混合使用时注意转换开销

功能说明:声明不同范围的整数类型,根据数值范围选择合适类型。

# 有符号整数
cdef int i = 100 # 32位,范围 -2147483648 ~ 2147483647
cdef long l = 1000000 # 平台相关(32位或64位)
cdef long long ll = 10000000000 # 64位,范围约 ±9.2×10^18
# 无符号整数
cdef unsigned int ui = 400 # 32位,范围 0 ~ 4294967295
cdef unsigned long ul = 1000 # 平台相关
cdef unsigned long long ull = 10000000 # 64位
# 短整数 - 内存敏感场景
cdef short s = 100 # 16位,范围 -32768 ~ 32767
cdef unsigned short us = 200 # 16位,范围 0 ~ 65535

选择建议:

类型使用场景
int一般整数运算(最常用)
long平台相关,32位系统用64位
long long大数值(超过20亿)
short仅在内存极度敏感时使用

功能说明:声明不同精度的浮点数,影响计算精度和性能。

# 单精度浮点 - 内存节省
cdef float f = 3.14159f # 32位,约7位有效数字
# 双精度浮点 - 常用选择
cdef double d = 3.141592653589793 # 64位,约15位有效数字
# 扩展精度 - 高精度需求
cdef long double ld = 3.14159265358979323846 # 80位或更高(平台相关)
# 示例:精度对比
cdef float f1 = 1.12345678901234567890
cdef double d1 = 1.12345678901234567890
# 输出:f1 ≈ 1.1234567(精度丢失)
# 输出:d1 ≈ 1.1234567890123457(完整精度)

输出示例:

>>> f1
1.1234567
>>> d1
1.1234567890123457

最佳实践:一般使用double,内存敏感或性能敏感时用float。

功能说明:用于非负数存储或位操作,提升表达明确性。

# 非负数存储 - 更明确的语义
cdef unsigned int size = 100
cdef unsigned long file_size = 1024000
# 位操作常用无符号
cdef unsigned int flags = 0b1100 # 12
cdef unsigned int mask = 0b0011 # 3
cdef unsigned int result = flags & mask # 0b0000 = 0

功能说明:声明C级指针,直接操作内存地址。

# 指针声明
cdef int *ptr # 指向int的指针
cdef double *d_ptr # 指向double的指针
cdef int **ptr_to_ptr # 指向指针的指针
# 获取变量地址
cdef int value = 42
cdef int *p = &value
# 解引用访问
print(p[0]) # 42,等同于*p
print(p[1]) # 危险!越界访问,内存不确定
# 数组与指针
cdef int arr[5] = [1, 2, 3, 4, 5]
cdef int *ap = arr
print(ap[0]) # 1
print(ap[1]) # 2
print((ap + 2)[0]) # 3,等同于ap[2]

输出示例:

>>> p[0]
42
>>> ap[0]
1
>>> ap[2]
3

常见坑:

  • 指针运算不检查边界,容易越界
  • 指向已释放内存的指针是未定义行为
  • 用typed memoryview替代手动指针更安全

功能说明:使用sizeof获取类型字节大小,理解平台差异。

# sizeof操作符
cdef int i
cdef long l
cdef long long ll
print(sizeof(i)) # 输出:4(字节)
print(sizeof(l)) # 输出:4或8(取决于平台)
print(sizeof(ll)) # 输出:8
# Py_ssize_t - Python索引类型(有符号)
cdef Py_ssize_t index # 有符号,匹配平台指针大小
# size_t - 无符号计数类型
cdef size_t count # 无符号,用于元素计数

输出示例:

>>> sizeof(i)
4
>>> sizeof(ll)
8
>>> sizeof(Py_ssize_t)
8 # 64位平台

功能说明:声明通用Python对象容器,可存储任意Python类型。

# 通用对象类型
cdef object obj
obj = 42 # Python int
obj = "hello" # Python str
obj = [1, 2, 3] # Python list
# 类型检查
if isinstance(obj, int):
print("整数:", obj)

输出示例:

>>> obj = 42
>>> isinstance(obj, int)
True
>>> obj = "hello"
>>> isinstance(obj, str)
True

功能说明:声明具体Python类型,帮助Cython优化并捕获类型错误。

# 强类型列表 - 仅允许list
cdef list int_list
cdef list str_list
int_list = [1, 2, 3] # OK
# int_list = "hello" # 编译警告(但仍可运行)
# 强类型字典
cdef dict str_to_int
str_to_int = {"a": 1, "b": 2}
# 强类型字符串
cdef str name
name = "Alice"

最佳实践:使用强类型声明让Cython生成更优化的代码,并提前发现类型不匹配。

功能说明:安全地在Python对象和具体类型之间转换。

# isinstance检查后转换
cdef object x = "hello"
if isinstance(x, str):
cdef str s = <str>x # 安全转换,编译时检查
print(s.upper())
# Cython编译时类型检查示例
cdef list lst = [1, 2, 3]
# lst.append("string") # 编译警告,但可能仍运行

输出示例:

>>> s.upper()
'HELLO'

功能说明:定义复合数据类型,将多个相关字段组合在一起。

# 定义结构体
ctypedef struct Point:
double x
double y
ctypedef struct Rectangle:
Point top_left
Point bottom_right
# 使用结构体
cdef Point p
p.x = 1.0
p.y = 2.0
cdef Rectangle rect
rect.top_left.x = 0.0
rect.top_left.y = 0.0
rect.bottom_right.x = 10.0
rect.bottom_right.y = 5.0
# 聚合初始化
cdef Point p2 = Point(3.0, 4.0)

输出示例:

>>> p.x
1.0
>>> rect.bottom_right.x
10.0
>>> p2.y
4.0

功能说明:结构体可以包含其他结构体,形成复杂数据类型。

ctypedef struct Size:
int width
int height
ctypedef struct Position:
int x
int y
ctypedef struct Window:
Position pos
Size size
str title
cdef Window win
win.pos.x = 100
win.pos.y = 200
win.size.width = 800
win.size.height = 600
win.title = "My Window"

最佳实践:嵌套结构体清晰表达数据层次,避免使用过多扁平字段。

功能说明:联合体所有字段共享同一内存,适用于同一数据不同解释场景。

# 联合体 - 所有字段共享同一内存
ctypedef union Data:
int int_val
double double_val
char bytes[8]
cdef Data data
data.int_val = 42
print(data.int_val) # 42
data.double_val = 3.14 # 覆盖int_val的内存
# print(data.int_val) # 危险!内存已被覆盖,结果不确定

常见坑:写入一个字段,读取另一个字段是未定义行为。联合体主要用于:

  • 同一数据不同解释(如4字节转int)
  • 内存复用(不同字段不同时使用)

功能说明:定义命名整数常量集合,提高代码可读性。

# 枚举定义
cdef enum Color:
RED = 0
GREEN = 1
BLUE = 2
cdef enum Status:
PENDING
RUNNING
COMPLETED
FAILED
# PENDING=0, RUNNING=1, COMPLETED=2, FAILED=3(自动递增)
# 使用枚举
cdef Color c = RED
if c == GREEN:
print("Green")

输出示例:

>>> c = RED
>>> c == GREEN
False
>>> Status.PENDING
0
>>> Status.RUNNING
1

功能说明:定义可在Python和Cython中访问的常量。

# cpdef常量 - Python和Cython均可访问
cpdef int MAX_SIZE = 1000
cpdef double PI = 3.141592653589793
cpdef str APP_NAME = "MyApp"
# C级常量 - 仅Cython可访问
cdef readonly int BUFFER_SIZE = 4096

最佳实践:cpdef用于需要在Python端使用的常量,cdef readonly用于仅Cython内部使用的常量。

功能说明:使用DEF定义编译时常量,生成高效代码。

# 引用C标准库宏
cdef extern from "math.h":
double M_PI
# 定义Cython宏(编译时求值)
DEF MAX_ITERATIONS = 1000
DEF EPSILON = 1e-10
cdef int i
for i in range(MAX_ITERATIONS): # 编译时替换为1000
# ...
pass

输出示例:

>>> MAX_ITERATIONS
1000
>>> EPSILON
1e-10

功能说明:为复杂类型创建简短别名,提高代码可读性。

# 简化指针类型
ctypedef double* DoublePtr
ctypedef int* IntPtr
cdef DoublePtr p1
cdef IntPtr p2
# 函数指针类型
ctypedef int (*CompareFunc)(int, int)

最佳实践:当类型名过长或复杂时使用typedef,如函数指针、嵌套结构体。

功能说明:用typedef简化typed memoryview等复杂类型声明。

# typed memoryview简化
ctypedef double[:, :] MatrixView # 2D矩阵视图
ctypedef double[::1] Vector # 1D连续向量
cdef void process_vector(Vector vec):
cdef int i
for i in range(len(vec)):
vec[i] *= 2.0

输出示例:

>>> vec = np.array([1.0, 2.0, 3.0])
>>> process_vector(vec)
>>> vec
array([2., 4., 6.])

功能说明:理解Cython的类型推断机制,避免隐式类型开销。

# Cython推断未声明变量为Python对象
x = 10 # 推断为int(Python对象,非C int)
y = 3.14 # 推断为float(Python对象)
# 显式声明获得C级性能
cdef float z = 3.14 # 明确为C float
# 混合类型警告
cdef int a = 10
cdef double b = a # int自动转为double(安全)

常见坑:

  • 未声明的变量默认是Python对象,有额外开销
  • Python对象和C类型混合计算需要转换

C类型大小精度/范围
int32位-2^31 ~ 2^31-1
long平台相关平台相关
long long64位-2^63 ~ 2^63-1
short16位-32768 ~ 32767
float32位约7位有效数字
double64位约15位有效数字
Py_ssize_t平台相关有符号,适合索引
场景推荐类型
一般整数int
大数值long long
货币计算使用int(分)或Decimal
浮点计算double(默认)
高精度long double
内存敏感float或short

  1. 比较int、long、long long的sizeof值
  2. 定义一个表示复数的结构体,包含实部和虚部
  3. 编写使用指针遍历数组的函数,理解指针运算
  4. 创建枚举表示一周七天,用switch或if处理
  5. 使用typedef简化double[:,:]类型名为Matrix
  6. 尝试混合C类型和Python对象计算,观察隐式转换