← 返回首页
C

C扩展与ctypes

📂 python ⏱ 2 min 271 words

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

安全注意事项

  1. 内存管理:C代码中的内存泄漏会影响Python进程
  2. 类型检查:确保Python和C之间的类型匹配
  3. 异常处理:C异常不会自动转换为Python异常
  4. 线程安全:注意GIL和C代码的线程安全性

最佳实践

总结

C扩展为Python提供了强大的性能提升能力。ctypes适合简单调用,CFFI提供更现代的接口,而C扩展模块提供最佳性能和灵活性。选择合适的方法取决于项目需求和维护成本。