人人都会AI编程

27.2 C 扩展开发:Cython 编写混合代码、C 扩展模块

更新时间:2026-07-12

当性能分析工具告诉你某个 Python 函数是 CPU 瓶颈时,直接用 C/C++ 重写这部分逻辑是最彻底的加速方式之一。Python 生态里主要有两条路:传统 C 扩展Cython。前者更底层、更灵活,后者更接近 Python 语法、开发效率高。


传统 C 扩展模块

传统方式用 C 语言直接编写模块,通过 Python/C API 与解释器交互。官网文档的 Extending Python with C or C++ 就是标准教程。

  • 基本流程
  1. 用 C 实现函数,接收 PyObject args,解析参数,执行计算,返回 PyObject
  2. 定义方法表,把函数名和 C 函数指针绑定。
  3. 定义模块结构,填充模块名和方法表。
  4. 编写 setup.py 脚本,使用 distutils 编译成 .so(Linux/macOS)或 .pyd(Windows)文件。
  • 适合场景:对性能要求极致、需要精细内存控制、直接调用已有 C/C++ 库。但开发调试成本较高,容易写出内存泄漏或段错误。

Cython:更友好的混合代码方案

Cython 是一个编程语言和编译器,让你用接近 Python 的语法编写扩展,最终编译成 C 扩展模块。它极大地降低了 C 扩展开发的门槛,是实际生产中最常用的选择。

  • 核心用法
  1. .py 文件重命名为 .pyx(Cython 源文件)。
  2. .pyx 中逐步添加类型声明,让编译器生成高效 C 代码:
     def add(int a, int b):
         return a + b
     
  1. 编写 setup.py,用 Cython.Build.cythonize 编译 .pyx 文件:
     from setuptools import setup
     from Cython.Build import cythonize
     setup(ext_modules=cythonize("math_utils.pyx"))
     
  1. 运行 python setup.py build_ext --inplace 生成 .pyd/.so,然后 import math_utils 就像普通 Python 模块一样使用。
  • 类型声明加速原理:Python 每次做 a + b 都需要检查类型、查找 add 方法。当你声明 int a, int b,Cython 直接生成 C 的整数相加代码,省去所有动态派发开销。
  • 渐进式优化:可以先把 Python 代码改个后缀,跑通编译流程;然后逐步给热点变量和函数加类型声明,每次编译后测试性能提升。不必一上来就全部类型化。

实际建议与避坑

  • 从 Cython 起步:绝大多数时候,Cython 已经足够快,且开发效率远高于手写 C 扩展。只有当你需要调用复杂 C++ 模板库、或者 Cython 生成的代码还不够快时,再考虑传统 C 扩展或 pybind11 等方案。
  • 编译产物管理:生成的 .so/.pyd 文件与 Python 版本和平台绑定,部署时需要确保编译环境与目标环境一致(通常用 manylinux 或 Docker 构建)。
  • 配合 NumPy:Cython 对 NumPy 有直接支持,声明 cimport numpy as np 和内存视图可实现零拷贝数据传递,科学计算性能提升明显。
  • 调试:用 cython -a your_file.pyx 生成带颜色标注的 HTML 报告,黄色越深代表 Python 交互越多,优化目标越明确。

总之,写 Python 代码,得 C 语言速度——这就是 Cython 的定位。当遇到性能瓶颈时,先用 Cython 做加速尝试,往往能以最小改动换来显著收益。