第一章:Python调用C函数的5种方式大比拼,第3种最高效却鲜为人知

在高性能计算和系统级编程中,Python常需调用C语言编写的函数以提升执行效率。目前主流的实现方式有五种,各自在易用性、性能和开发成本上存在显著差异。

使用 ctypes 直接加载动态库

ctypes 是 Python 标准库的一部分,无需额外安装,适合快速调用已编译的 C 共享库。

1

2

3

4

5

6

# 编译命令: gcc -shared -fPIC -o libmath.so math.c

from ctypes import CDLL

lib = CDLL("./libmath.so")

result = lib.add(5, 3# 假设C中定义了 int add(int a, int b)

print(result)  # 输出: 8

该方法简单直接,但不支持复杂数据结构且缺乏类型安全检查。

借助 Cython 编写混合代码

Cython 将 Python 语法扩展为可编译为 C 的形式,允许精细控制类型。

1

2

3

4

5

6

# 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 实现原生 C 接口调用

CFFI 支持从 Python 中直接声明和调用 C 函数,兼容 C99 标准,是本章节中最高效且少被认知的方式。

1

2

3

4

5

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 生成跨语言绑定

SWIG 是老牌工具,能自动生成多种语言的接口包装,适用于大型项目。

通过 Python C 扩展手动编写模块

直接使用 Python C API 编写模块,性能最优但开发复杂度最高。

  • ctypes:零依赖,适合简单调用
  • Cython:高性能,适合算法加速
  • CFFI:高效且灵活,推荐现代项目使用
  • SWIG:适用于多语言集成
  • 原生C扩展:最大控制力,维护成本高
方式 性能 学习成本 适用场景
CFFI ★★★★★ ★★★ 高频调用、复杂接口
Cython ★★★★☆ ★★★★ 数值计算

第二章:主流调用方式详解与性能对比

2.1 ctypes接口调用:无需编译的便捷方案

在Python中直接调用C语言函数,ctypes提供了一种无需额外编译步骤的轻量级解决方案。它允许Python动态加载共享库,并以原生方式调用其中的函数。

基本使用流程

通过cdll加载动态链接库,即可访问导出的C函数:

1

2

3

4

5

6

from ctypes import cdll

# 加载 libc(Linux/Unix)

libc = cdll.LoadLibrary("libc.so.6")

# 调用 puts 函数

libc.puts(b"Hello from C!")

上述代码加载系统libc并调用其puts函数。参数需转换为C兼容类型,如字符串应传入字节对象(b"")。

数据类型映射

ctypes支持基础类型的自动转换:

  • c_int:对应C的int
  • c_char_p:字符指针,适用于字符串
  • POINTER(c_double):双精度数组指针

该机制避免了编写C扩展模块的复杂性,适用于快速集成已有C库。

2.2 CFFI实现动态调用:跨语言交互的新选择

CFFI(C Foreign Function Interface)为Python提供了高效调用C语言函数的能力,无需编写复杂的扩展模块。其核心优势在于支持直接加载共享库并动态绑定函数。

基本使用流程
  • 定义C函数声明或从头文件中解析
  • 使用ffi.dlopen()加载动态链接库
  • 通过FFI对象调用C函数,如同调用原生Python函数

1

2

3

4

5

from cffi import FFI

ffi = FFI()

ffi.cdef("int add(int, int);")

C = ffi.dlopen("./libadd.so")

result = C.add(3, 4# 调用C函数

上述代码中,cdef声明了C函数签名,dlopen加载本地共享库,随后即可在Python中直接调用。参数自动完成类型转换,简化了跨语言数据传递过程。

2.3 Cython封装C函数:编译级集成的高效路径

核心机制与优势

Cython通过生成C级别的扩展模块,实现Python对原生C函数的高效调用。其关键在于将Python代码编译为C,并与C库直接链接,消除解释层开销。

封装步骤示例

首先定义C函数头文件 math_utils.h

1

2

// math_utils.h

double add(double a, double b);

该函数接受两个双精度浮点数,返回其和,是典型的基础算术操作。 接着编写Cython包装文件 wrapper.pyx

1

2

3

4

5

6

# 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)

cdef extern 声明外部C函数接口,py_add 提供Python可调用的包装层。

  • 编译过程由setup.py驱动,生成.so动态库
  • 最终Python脚本可直接import py_add

2.4 使用SWIG生成绑定:多语言支持的经典工具

SWIG(Simplified Wrapper and Interface Generator)是一个强大的开源工具,能够将C/C++代码自动封装为多种高级语言接口,包括Python、Java、Ruby和Lua等。

基本工作流程

使用SWIG时,首先定义一个接口文件(.i),声明需要导出的函数与类型:

1

2

3

4

5

6

/* example.i */

%module example

%{

extern double multiply(double a, double b);

%}

extern double multiply(double a, double b);

该接口文件告诉SWIG哪些C++符号需要暴露。接着运行swig -python example.i,生成包装代码example_wrap.c和目标语言模块脚本。

支持的语言与特性对比
语言 线程安全 GC集成
Python 自动引用计数
Java JVM GC托管
Ruby 部分 Ruby GC

SWIG通过解析C/C++头文件并生成适配层,实现跨语言调用,极大简化了原生扩展开发。

2.5 原生Python/C API扩展:最底层但最灵活的方式

使用原生Python/C API是实现高性能扩展的终极手段,直接操作解释器对象结构,具备最高执行效率与最大控制粒度。

更多推荐