文章目录
Python中CFFI介绍和使用
CFFI (C Foreign Function Interface) 是Python的一个外部函数接口库,用于调用C代码。它提供了与C语言交互的简单方式,是替代ctypes的另一种选择。
CFFI的特点
- 支持两种模式:ABI模式和API模式
- 自动生成绑定代码:可以自动生成Python与C之间的桥梁代码
- 支持CPython和PyPy:在PyPy上性能表现优异
- 内存管理安全:自动管理内存,减少内存泄漏风险
- 支持C语言标准:支持C99和部分C11特性
安装CFFI
pip install cffi
使用模式
1. ABI模式 (应用程序二进制接口)
ABI模式不需要编译,直接加载动态库:
from cffi import FFI
ffi = FFI()
ffi.cdef("""
int printf(const char *format, ...);
""")
C = ffi.dlopen(None) # 加载标准C库
C.printf(b"Hello, %s!\n", b"World")
2. API模式 (应用程序接口)
API模式需要编译,性能更好:
from cffi import FFI
ffi = FFI()
ffi.cdef("""
int add(int a, int b);
""")
# 内联C源代码
ffi.set_source("_example",
"""
int add(int a, int b) {
return a + b;
}
""")
if __name__ == "__main__":
ffi.compile()
使用编译后的模块:
from _example import ffi, lib
result = lib.add(2, 3)
print(result) # 输出: 5
实际示例
示例1:调用标准C库函数
from cffi import FFI
ffi = FFI()
ffi.cdef("""
double sin(double x);
double cos(double x);
""")
C = ffi.dlopen(None) # 加载标准C库
x = 3.1415926535 / 4
print("sin:", C.sin(x))
print("cos:", C.cos(x))
示例2:与自定义C代码交互
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
int add(int a, int b);
void print_message(const char *message);
#endif
example.c
#include <stdio.h>
#include "example.h"
int add(int a, int b) {
return a + b;
}
void print_message(const char *message) {
printf("Message: %s\n", message);
}
编译为共享库:
gcc -shared -o libexample.so -fPIC example.c
Python代码:
from cffi import FFI
ffi = FFI()
ffi.cdef("""
int add(int a, int b);
void print_message(const char *message);
""")
lib = ffi.dlopen("./libexample.so")
print("Add result:", lib.add(5, 7))
lib.print_message(b"Hello from Python!")
示例3:处理复杂数据结构
from cffi import FFI
ffi = FFI()
ffi.cdef("""
typedef struct {
int x;
int y;
} Point;
double distance(Point p1, Point p2);
""")
ffi.set_source("_geometry",
"""
#include <math.h>
typedef struct {
int x;
int y;
} Point;
double distance(Point p1, Point p2) {
int dx = p1.x - p2.x;
int dy = p1.y - p2.y;
return sqrt(dx*dx + dy*dy);
}
""")
if __name__ == "__main__":
ffi.compile()
使用编译后的模块:
from _geometry import ffi, lib
p1 = ffi.new("Point *", [1, 2])
p2 = ffi.new("Point *", [4, 6])
distance = lib.distance(p1[0], p2[0])
print("Distance:", distance) # 输出: 5.0
内存管理
CFFI提供了几种内存管理方式:
- ffi.new():分配内存并在Python对象被垃圾回收时自动释放
- ffi.gc():显式指定垃圾回收时的释放函数
- ffi.release():手动释放内存
from cffi import FFI
ffi = FFI()
ffi.cdef("""
typedef struct {
char *name;
int age;
} Person;
Person *create_person(char *name, int age);
void free_person(Person *p);
""")
# 假设这是与C库交互的部分
ffi.set_source("_person", "")
if __name__ == "__main__":
ffi.compile()
from _person import ffi, lib
# 自动内存管理
person = ffi.new("Person *")
person.name = ffi.new("char[]", b"Alice")
person.age = 30
# 手动内存管理
p = lib.create_person(b"Bob", 25)
try:
print(ffi.string(p.name), p.age)
finally:
lib.free_person(p)
与NumPy集成
CFFI可以与NumPy数组高效交互:
import numpy as np
from cffi import FFI
ffi = FFI()
ffi.cdef("""
void square_array(double *array, int length);
""")
ffi.set_source("_numpy_example",
"""
void square_array(double *array, int length) {
for (int i = 0; i < length; i++) {
array[i] = array[i] * array[i];
}
}
""")
if __name__ == "__main__":
ffi.compile()
from _numpy_example import ffi, lib
arr = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float64)
ptr = ffi.cast("double *", arr.ctypes.data)
lib.square_array(ptr, len(arr))
print(arr) # 输出: [ 1. 4. 9. 16.]
性能考虑
- API模式比ABI模式更快
- 在PyPy上,CFFI的性能通常比CPython更好
- 尽量减少Python和C之间的数据传递
- 对于大量数据处理,考虑使用内存视图(ffi.from_buffer)
常见问题
- 类型转换:注意Python和C类型之间的差异
- 内存管理:确保正确管理内存,避免泄漏
- 线程安全:CFFI调用通常是线程安全的,但底层C库可能不是
- 错误处理:检查C函数的返回值,处理错误情况
总结
CFFI是Python与C代码交互的强大工具,提供了比ctypes更友好、更安全的接口。它特别适合:
- 需要高性能计算的场景
- 与现有C库集成
- 在PyPy环境下运行代码
- 需要自动内存管理的场景
通过合理使用CFFI,可以充分发挥Python的灵活性和C的性能优势。