Skip to main content

(编写中)🐏Python与C互交互

非常重要!

概述

Python与C语言的互操作是高性能计算和系统编程的关键技术。主要有三种方式:

  1. ctypes - 直接调用C库
  2. C扩展模块 - 编写Python C API扩展
  3. Cython - 混合Python和C语法的编译器

ctypes集成

基本使用

from ctypes import CDLL, c_int, c_double, c_char_p

# 加载C库
lib = CDLL('./mylib.so')

# 定义函数原型
lib.add.argtypes = [c_int, c_int]
lib.add.restype = c_int

# 调用C函数
result = lib.add(5, 3)
print(f"5 + 3 = {result}")

结构体处理

// example.h
typedef struct {
int x;
int y;
char name[50];
} Point;

extern void print_point(Point *p);
from ctypes import Structure, c_int, c_char, CDLL

class Point(Structure):
_fields_ = [
("x", c_int),
("y", c_int),
("name", c_char * 50)
]

lib = CDLL('./example.so')
point = Point(10, 20, b"test")
lib.print_point(point)

C扩展模块开发

基本结构

#include <Python.h>

static PyObject* 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", add, METH_VARARGS, "Add two numbers"},
{NULL, NULL, 0, NULL}
};

static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"myextension",
NULL,
-1,
methods
};

PyMODINIT_FUNC PyInit_myextension(void) {
return PyModule_Create(&module);
}

编译setup.py

from distutils.core import setup, Extension

module = Extension(
'myextension',
sources=['myextension.c']
)

setup(
name='MyExtension',
version='1.0',
ext_modules=[module]
)

Cython实战

基本语法

# mymodule.pyx
def add(int a, int b):
cdef int result = a + b
return result

编译setup.py

from distutils.core import setup
from Cython.Build import cythonize

setup(
ext_modules=cythonize("mymodule.pyx")
)

内存管理最佳实践

引用计数

// 正确增加引用计数
Py_INCREF(obj);

// 正确减少引用计数
Py_DECREF(obj);

// 借用引用(不需要管理引用计数)
PyObject* item = PyList_GetItem(list, index);

内存泄漏检测

使用Valgrind检测内存泄漏:

valgrind --leak-check=full python script.py

错误处理

C错误转Python异常

static PyObject* risky_function(PyObject* self, PyObject* args) {
// 某些可能失败的操作
if (operation_failed) {
PyErr_SetString(PyExc_RuntimeError, "Operation failed");
return NULL;
}

// 成功返回
Py_RETURN_NONE;
}

性能优化技巧

避免不必要的转换

// 不好的做法:频繁创建Python对象
for (int i = 0; i < 1000000; i++) {
PyObject* num = PyLong_FromLong(i);
// 使用num
Py_DECREF(num);
}

// 好的做法:在C层面处理数据
int result = 0;
for (int i = 0; i < 1000000; i++) {
result += i;
}
PyObject* final = PyLong_FromLong(result);

多线程与GIL

释放GIL

Py_BEGIN_ALLOW_THREADS
// 执行耗时操作(文件I/O、网络请求等)
perform_time_consuming_operation();
Py_END_ALLOW_THREADS

调试技巧

GDB调试C扩展

gdb --args python script.py
(gdb) break myextension.c:25
(gdb) run

常见陷阱

  1. 引用计数错误:忘记INCREF/DECREF
  2. GIL问题:在错误的时间持有或释放GIL
  3. 类型转换错误:错误的参数类型解析
  4. 内存泄漏:未正确释放分配的内存

实战案例

图像处理扩展

// 快速的图像旋转函数
static PyObject* rotate_image(PyObject* self, PyObject* args) {
PyObject* image_obj;
double angle;

if (!PyArg_ParseTuple(args, "Od", &image_obj, &angle)) {
return NULL;
}

// 获取图像数据指针
Py_buffer buffer;
if (PyObject_GetBuffer(image_obj, &buffer, PyBUF_SIMPLE) != 0) {
return NULL;
}

// 执行旋转操作
rotate_image_data(buffer.buf, buffer.len, angle);

PyBuffer_Release(&buffer);
Py_RETURN_NONE;
}

工具链推荐

  • 编译: GCC/Clang + distutils/setuptools
  • 调试: GDB + Python调试器
  • 性能分析: perf, gprof, cProfile
  • 内存检测: Valgrind, AddressSanitizer

最佳实践总结

  1. 优先使用Cython,语法更Pythonic
  2. 复杂项目使用C扩展模块
  3. 简单调用使用ctypes
  4. 始终注意内存管理和引用计数
  5. 在多线程环境中正确处理GIL
  6. 编写全面的错误处理代码
  7. 进行充分的测试和性能分析