Python 调用 C 函数的 5 种方式对比与性能优化
在高性能计算和系统级编程中,Python 常需调用 C 语言编写的函数以提升执行效率。目前主流的实现方式有五种,各自在易用性、性能和开发成本上存在显著差异。
1. 五种方式概览
| 方式 | 性能 | 学习成本 | 适用场景 |
|---|---|---|---|
本文对比了 Python 调用 C 函数的五种主流方式(ctypes、Cython、CFFI、SWIG、原生 C 扩展),分析了各自的性能、学习成本及适用场景。重点介绍了 CFFI 的高效性与灵活性,并提供了 GIL 竞争处理、内存管理最佳实践及 setuptools 打包等实战技巧,帮助开发者在高性能计算中选择合适的集成方案。
在高性能计算和系统级编程中,Python 常需调用 C 语言编写的函数以提升执行效率。目前主流的实现方式有五种,各自在易用性、性能和开发成本上存在显著差异。
| 方式 | 性能 | 学习成本 | 适用场景 |
|---|---|---|---|
| ctypes |
| ★★★ |
| ★★ |
| 简单调用,零依赖 |
| Cython | ★★★★☆ | ★★★★ | 数值计算,算法加速 |
| CFFI | ★★★★★ | ★★★ | 高频调用,复杂接口 |
| SWIG | ★★★★ | ★★★ | 多语言集成 |
| 原生 C 扩展 | ★★★★★ | ★★★★★ | 最大控制力,维护成本高 |
ctypes 是 Python 标准库的一部分,无需额外安装,适合快速调用已编译的 C 共享库。
from ctypes import CDLL
lib = CDLL("./libmath.so")
result = lib.add(5, 3)
print(result) # 输出:8
该方法简单直接,但不支持复杂数据结构且缺乏类型安全检查。
Cython 将 Python 语法扩展为可编译为 C 的形式,允许精细控制类型。
# example.pyx
def fast_sum(int n):
cdef int i, total = 0
for i in range(n):
total += i
return total
通过配置 setup.py 并运行构建指令,生成可导入的模块,性能接近原生 C。
CFFI 支持从 Python 中直接声明和调用 C 函数,兼容 C99 标准,兼顾灵活性与速度。
from cffi import FFI
ffi = FFI()
ffi.cdef("int add(int a, int b);")
C = ffi.dlopen("./libmath.so")
print(C.add(7, 9)) # 输出:16
其优势在于支持回调函数、指针操作,并可在 ABI 与 API 模式间切换。
SWIG 是老牌工具,能自动生成多种语言的接口包装,适用于大型项目。
/* example.i */
%module example
%{
extern double multiply(double a, double b);
%}
extern double multiply(double a, double b);
该接口文件告诉 SWIG 哪些 C++ 符号需要暴露。接着运行 swig -python example.i,生成包装代码和目标语言模块脚本。
直接使用 Python C API 编写模块,性能最优但开发复杂度最高。
#include <Python.h>
static PyObject* py_add(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
return PyLong_FromLong(a + b);
}
// ... 注册模块逻辑 ...
在 Python 中直接调用 C 语言函数,ctypes 提供了一种无需额外编译步骤的轻量级解决方案。
from ctypes import cdll
libc = cdll.LoadLibrary("libc.so.6")
libc.puts(b"Hello from C!")
参数需转换为 C 兼容类型,如字符串应传入字节对象(b"")。ctypes 支持基础类型的自动转换,如 c_int、c_char_p 等。
CFFI 的核心优势在于支持直接加载共享库并动态绑定函数。
from cffi import FFI
ffi = FFI()
ffi.cdef("int add(int, int);")
C = ffi.dlopen("./libadd.so")
result = C.add(3, 4)
参数自动完成类型转换,简化了跨语言数据传递过程。
Cython 通过生成 C 级别的扩展模块,实现 Python 对原生 C 函数的高效调用。
首先定义 C 函数头文件 math_utils.h:
double add(double a, double b);
接着编写 Cython 包装文件 wrapper.pyx:
cdef extern from "math_utils.h":
double add(double a, double b)
def py_add(double x, double y):
return add(x, y)
编译过程由 setup.py 驱动,最终 Python 脚本可直接 import py_add。
使用原生 Python/C API 是实现高性能扩展的终极手段,直接操作解释器对象结构。
#include <Python.h>
static PyObject* py_add(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
return PyLong_FromLong(a + b);
}
static PyMethodDef methods[] = {
{"add", py_add, METH_VARARGS, "Add two integers"},
{NULL}
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT, "fastmath", NULL, -1, methods
};
PyMODINIT_FUNC PyInit_fastmath(void) {
return PyModule_Create(&module);
}
全局解释器锁(GIL)限制了多线程程序的并行执行能力。为减少 GIL 竞争,应优先使用 I/O 密集型任务的多线程模型。
import threading
import time
def io_task(duration):
time.sleep(duration) # 模拟 I/O 阻塞,GIL 在此期间被释放
print(f"Thread {threading.get_ident()} completed")
threads = [threading.Thread(target=io_task, args=(1,)) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
在使用 malloc、calloc 或 new 分配内存后,必须确保在不再使用时调用 free 或 delete。推荐使用智能指针(C++)自动管理生命周期。
#include <memory>
std::unique_ptr<int> data = std::make_unique<int>(42);
使用 setuptools 构建和分发第三方库。创建 setup.py 文件定义包的元信息。
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1.0",
packages=find_packages(),
install_requires=["requests>=2.25.0"]
)
段错误(Segmentation Fault)通常由指针操作不当或引用计数配对错误引起。
PyObject *obj = NULL;
Py_INCREF(obj); // 错误:对 NULL 指针增加引用计数
正确做法是确保对象非 NULL 后再操作引用计数。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online