C扩展与ctypes
C扩展与ctypes
Python作为高级语言,在某些计算密集型任务上性能受限。通过C扩展,我们可以调用C语言编写的库,大幅提升性能。本文将介绍三种主要方法:ctypes、CFFI和编写C扩展。
ctypes基础
ctypes是Python标准库中的外部函数接口,可以直接调用动态链接库:
import ctypes
import time
# 加载C标准库(Windows上是msvcrt.dll)
libc = ctypes.CDLL("msvcrt" if __name__ == "__main__" else "libc.so.6")
# 调用C函数
libc.printf(b"Hello from C!\n")
# 定义函数签名
libc.time.restype = ctypes.c_long
current_time = libc.time(None)
print(f"C时间函数返回: {current_time}")
传递复杂数据类型
import ctypes
# 定义C结构体
class Point(ctypes.Structure):
_fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
# 创建数组
DoubleArray = ctypes.c_double * 10
arr = DoubleArray(*range(10))
# 指针操作
ptr = ctypes.pointer(arr)
print(f"数组第一个元素: {ptr.contents[0]}")
性能对比示例
import ctypes
import time
def python_factorial(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
def ctypes_factorial(n):
libmath = ctypes.CDLL("libm.so.6") # Linux
libmath.tgamma.restype = ctypes.c_double
libmath.tgamma.argtypes = [ctypes.c_double]
return int(libmath.tgamma(n + 1))
n = 100000
start = time.time()
python_result = python_factorial(n)
python_time = time.time() - start
start = time.time()
c_result = ctypes_factorial(n)
c_time = time.time() - start
print(f"Python: {python_time:.4f}s, C: {c_time:.4f}s")
print(f"加速比: {python_time/c_time:.1f}x")
CFFI入门
CFFI比ctypes更灵活,支持两种模式:
# ABI模式(类似ctypes)
from cffi import FFI
ffi = FFI()
ffi.cdef("int printf(const char *format, ...);")
lib = ffi.dlopen(None)
lib.printf(b"Hello CFFI!\n")
# API模式(需要编译)
ffi = FFI()
ffi.cdef("""
double sqrt(double x);
double pow(double base, double exp);
""")
lib = ffi.dlopen("libm.so.6")
print(f"sqrt(2) = {lib.sqrt(2.0)}")
编写Python C扩展
最灵活的方式是编写C扩展模块:
// mymodule.c
#include <Python.h>
static PyObject* add_numbers(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", add_numbers, METH_VARARGS, "Add two integers"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT, "mymodule", NULL, -1, methods
};
PyMODINIT_FUNC PyInit_mymodule(void) {
return PyModule_Create(&module);
}
构建扩展
使用distutils构建扩展:
# setup.py
from distutils.core import setup, Extension
module = Extension('mymodule', sources=['mymodule.c'])
setup(name='mymodule', version='1.0', ext_modules=[module])
python setup.py build_ext --inplace
安全注意事项
- 内存管理:C代码中的内存泄漏会影响Python进程
- 类型检查:确保Python和C之间的类型匹配
- 异常处理:C异常不会自动转换为Python异常
- 线程安全:注意GIL和C代码的线程安全性
最佳实践
- 优先使用现有的C库(如NumPy)
- 尽量减少Python-C边界调用次数
- 使用ctypes进行快速原型开发
- 高性能需求考虑CFFI API模式
- 生产环境使用C扩展或Cython
总结
C扩展为Python提供了强大的性能提升能力。ctypes适合简单调用,CFFI提供更现代的接口,而C扩展模块提供最佳性能和灵活性。选择合适的方法取决于项目需求和维护成本。