主要有三种主流方法,我会按照从最推荐到最特定场景的顺序进行详细讲解:

- 使用 CFFI (C Foreign Function Interface) - 现代、推荐的首选
- 使用 ctypes (Python 内置库) - 简单、无需编译,适合快速调用
- 使用 Cython - 将 Python 代码编译为 C 扩展,性能极高
使用 CFFI (C Foreign Function Interface)
CFFI 是一个现代、功能强大的 Python 包,用于与 C 代码交互,它提供了两种调用模式:ABI (Application Binary Interface) 和 API (Application Programming Interface)。
- ABI 模式:直接编译和链接 C 代码,生成一个 DLL (
.dll) 或共享库 (.so),Python 加载这个库,这是最常用、最简单的方式。 - API 模式:在编译时就需要 C 编译器和 Python 的头文件 (
Python.h),类似于 Cython,更复杂,但功能更强大。
这里我们重点讲解最常用的 ABI 模式。
步骤 1: 编写 C 代码
创建一个名为 my_c_functions.c 的文件。
// my_c_functions.c
#include <stdio.h>
// 一个简单的加法函数
int add(int a, int b) {
printf("C 函数被调用: add(%d, %d)\n", a, b);
return a + b;
}
// 一个打印字符串的函数
void print_message(const char* message) {
printf("C 函数打印: %s\n", message);
}
步骤 2: 编译 C 代码为 DLL (Windows)
你需要一个 C 编译器,MinGW (GCC for Windows) 或 Visual Studio 的 cl.exe,这里以 MinGW 为例。

-
安装 MinGW: 你可以从 MSYS2 或 MinGW-w64 安装,安装后,确保
gcc命令在系统PATH中。 -
编译命令: 打开命令行 (CMD 或 PowerShell),进入到
my_c_functions.c所在的目录,然后执行:# -o: 指定输出文件名 # -shared: 生成共享库 (DLL) # -O2: 基本的优化选项 gcc my_c_functions.c -o my_c_functions.dll -shared -O2
执行成功后,你会得到
my_c_functions.dll文件。
步骤 3: 在 Python 中使用 CFFI 调用 DLL
安装 CFFI 库:

pip install cffi
创建一个 Python 脚本,main.py:
# main.py
from cffi import FFI
# 1. 定义 C 函数的接口
ffi = FFI()
# cdef() 用于声明 C 语言的函数和变量
ffi.cdef("""
int add(int a, int.b);
void print_message(const char *message);
""")
# 2. 加载编译好的 DLL 库
# Windows 下使用 'dllname',Linux/macOS 下使用 'libname.so'
try:
# 假设 my_c_functions.dll 和 main.py 在同一目录
c_lib = ffi.dlopen("./my_c_functions.dll")
except OSError as e:
print(f"错误: 无法加载 DLL. 请确保 my_c_functions.dll 在同一目录下.")
print(f"详细错误: {e}")
exit()
# 3. 调用 C 函数
# CFFI 会自动处理 Python int 和 C int,以及 Python str 和 C char* 之间的转换
# 调用 add 函数
result = c_lib.add(10, 25)
print(f"Python 收到返回值: {result}")
# 调用 print_message 函数
# 注意:Python 字符串需要转换为 C 字符串
c_lib.print_message("你好,来自 Python 的世界!")
运行 python main.py,你将看到如下输出:
C 函数被调用: add(10, 25)
Python 收到返回值: 35
C 函数打印: 你好,来自 Python 的世界!
使用 ctypes (Python 内置库)
ctypes 是 Python 的标准库,允许你调用 DLL 中的函数,它非常简单,不需要额外的安装,但代码风格可能不如 CFFI 现代。
步骤 1 & 2: 与 CFFI 相同
确保你已经有了 my_c_functions.c 和编译好的 my_c_functions.dll。
步骤 3: 在 Python 中使用 ctypes
创建 main_ctypes.py:
# main_ctypes.py
import ctypes
# 1. 加载 DLL
try:
# Windows 下使用 cdll.LoadLibrary
c_lib = ctypes.CDLL("./my_c_functions.dll")
except OSError as e:
print(f"错误: 无法加载 DLL. 请确保 my_c_functions.dll 在同一目录下.")
print(f"详细错误: {e}")
exit()
# 2. 定义函数的参数和返回值类型 (非常重要!)
# 如果不定义,ctypes 可能会错误地解释内存,导致程序崩溃
# 为 add 函数设置参数类型和返回类型
c_lib.add.argtypes = [ctypes.c_int, ctypes.c_int]
c_lib.add.restype = ctypes.c_int
# 为 print_message 函数设置参数类型和返回类型 (void -> None)
c_lib.print_message.argtypes = [ctypes.c_char_p] # C 的 const char* 对应 ctypes.c_char_p
c_lib.print_message.restype = None
# 3. 调用 C 函数
# 调用 add 函数
result = c_lib.add(100, 200)
print(f"Python 收到返回值: {result}")
# 调用 print_message 函数
# Python 字符串需要编码为 bytes (C 风格的字符串)
# 使用 .encode('utf-8') 是最安全的方式
message_bytes = "你好,ctypes!".encode('utf-8')
c_lib.print_message(message_bytes)
运行 python main_ctypes.py,输出如下:
C 函数被调用: add(100, 200)
Python 收到返回值: 300
C 函数打印: 你好,ctypes!
使用 Cython
Cython 的目标不是简单地“调用”C,而是让你用一种类似 Python 的语法(Cython 语言)来编写代码,Cython 将其编译成 C 代码,最后再编译成 Python 可以导入的模块(一个 .pyd 文件,在 Windows 上等价于 DLL),这种方法最适合将计算密集型的 Python 代码“翻译”成高性能的 C 代码。
步骤 1: 编写 Cython 代码 (.pyx 文件)
创建一个名为 my_module.pyx 的文件。
# my_module.pyx
# 这是一个 Cython 文件,语法是 Python + C 类型声明
# 导入 C 库中的函数
# 注意:这里我们声明 extern C 函数,就像在 C 中一样
cdef extern from "my_c_functions.h":
int add(int a, int b)
void print_message(const char *message)
# 定义一个 Python 可以调用的函数
def call_c_functions():
print("--- 从 Cython 模块调用 C 函数 ---")
result = add(5, 7)
print(f"C 函数 add 返回: {result}")
print_message("这条消息来自 Cython 调用。")
return result
最佳实践:创建一个 C 头文件
为了让 Cython 知道 C 函数的签名,创建一个 my_c_functions.h 文件。
// my_c_functions.h #ifndef MY_C_FUNCTIONS_H #define MY_C_FUNCTIONS_H int add(int a, int b); void print_message(const char *message); #endif
步骤 2: 编译 Cython 代码为 .pyd 文件
你需要安装 Cython 和一个 C 编译器。
-
安装 Cython:
pip install cython
-
创建
setup.py文件: 这是编译脚本。# setup.py from setuptools import setup from Cython.Build import cythonize import os # 告诉编译器去哪里找我们的 C 源文件 extra_compile_args = [ f'-I{os.path.abspath(".")}' # 将当前目录包含在头文件搜索路径中 ] setup( ext_modules = cythonize("my_module.pyx"), extra_compile_args=extra_compile_args ) -
执行编译:
python setup.py build_ext --inplace
执行后,你会得到一个
my_module.cp3x-win_amd64.pyd(版本号和架构可能不同) 的文件。
步骤 3: 在 Python 中导入并使用
创建 main_cython.py:
# main_cython.py # 像导入普通 Python 模块一样导入编译好的 .pyd 文件 import my_module # 调用我们在 my_module.pyx 中定义的函数 my_module.call_c_functions()
运行 python main_cython.py,输出如下:
--- 从 Cython 模块调用 C 函数 ---
C 函数被调用: add(5, 7)
C 函数 add 返回: 12
C 函数打印: 这条消息来自 Cython 调用。
总结与对比
| 特性 | CFFI | ctypes | Cython |
|---|---|---|---|
| 易用性 | 高 (ABI模式) | 高 (非常简单) | 中 (需要学习 Cython 语法) |
| 性能 | 高 (接近原生) | 高 (接近原生) | 极高 (代码本身就是 C) |
| 主要用途 | 调用外部 C 库 | 快速调用外部 C 库 | 将 Python 代码编译为高性能扩展 |
| 依赖 | pip install cffi |
Python 内置 | pip install cython, C 编译器 |
| 工作流 | C -> DLL -> Python 调用 | C -> DLL -> Python 调用 | Cython (.pyx) -> C -> .pyd -> Python 导入 |
| 类型检查 | 在 Python 端定义 cdef |
在 Python 端定义 argtypes |
在 Cython 代码中静态声明 |
| 推荐场景 | 大多数情况下的首选,特别是当你的核心逻辑在 C 库中时。 | 快速原型验证或当无法安装 cffi 时。 |
优化现有 Python 代码,或开发需要极致性能的 Python 扩展。 |
最终建议
- 如果你只是想调用一个现成的 C/C++ 库:强烈推荐使用 CFFI,它的设计更现代,API 更友好,并且性能很好。
- 如果你只是想快速测试一个简单的 C 函数,并且不想安装任何第三方包:
ctypes是你的不二之选。 - 如果你的 Python 程序有一个计算瓶颈,你想让它运行得更快:学习并使用 Cython,这是将 Python 性能提升到 C 级别的标准方法。
