如何在 Python 中调用 C 函数?

2025年1月5日 | 阅读6分钟

Python 和 C 是两种广为人知的编程语言,它们具有不同的特性和优势。Python 以其简洁性、可读性和高级抽象而闻名,是快速开发和原型设计的绝佳选择。另一方面,C 因其速度、效率和对系统资源的底层控制而受到重视,使其在性能关键型应用程序和系统编程中不可或缺。

虽然 Python 在可用性和灵活性方面表现出色,但它可能无法始终满足某些任务的性能要求。这时,集成 C 代码就变得至关重要。通过从 Python 调用 C 函数,开发人员可以利用 C 的性能优势,同时保留 Python 的高级表达能力和效率。这种组合为从科学计算和数值分析到系统编程和嵌入式开发等各种应用提供了强大的解决方案。

动机

从 Python 调用 C 函数的根本目的是利用这两种语言的优势。

  1. 性能优化:虽然 Python 以其简洁性和易用性而闻名,但它可能无法始终提供计算密集型任务所需的性能。通过将性能关键型操作卸载到 C 函数,开发人员可以在不牺牲 Python 代码的可读性和可维护性的情况下实现显著的性能提升。
  2. 访问现有库:许多强大的库和框架是用 C 编写的,或者具有 C 绑定。通过从 Python 调用 C 函数,开发人员可以利用这个庞大的现有功能环境,而无需在 Python 中重新发明轮子。
  3. 平台特定功能:某些系统级功能,例如与硬件交互或访问底层系统 API,只能通过 C 库获得。通过从 Python 调用这些函数,开发人员可以访问 Python 标准库未直接公开的平台特定功能。
  4. 嵌入式系统编程:在内存和处理能力通常有限的嵌入式系统编程中,从 Python 调用 C 函数对于实现最佳性能和资源利用率至关重要。通过与 C 代码交互,开发人员可以利用 C 的效率,同时仍然受益于 Python 的高级抽象。

使用 ctypes 进行基本交互

ctypes 是 Python 标准库中一个强大的库,它允许您直接从 Python 代码调用共享库(DLL)中的函数。它为 Python 提供了一个外部函数接口(FFI),可以与 C 代码无缝集成。ctypes 因其简单性和易用性而广受欢迎,是执行基本 C 交互任务的绝佳选择。

入门

让我们通过一个使用 ctypes 从 Python 调用 C 函数的简单示例。假设我们有一个简单的 C 函数,用于将两个整数相加。

我们将此 C 代码编译为共享库(在类 Unix 系统上)。

现在,我们可以使用 ctypes 从 Python 调用此函数。

输出

Result of addition: 8

在此示例中,我们使用 ctypes.CDLL() 加载了包含我们的 C 函数的共享库。然后,我们从库中访问了 add 函数,并用两个整数参数调用了它。

参数和返回类型

ctypes 允许您在 Python 中指定 C 函数的参数类型和返回类型,从而确保类型安全以及 Python 和 C 数据类型之间的正确转换。以下是为 add 函数指定参数和返回类型的方法。

输出

Result of expansion: 8

这告诉 ctypes add 函数接受两个整数参数(ctypes.c_int)并返回一个整数(ctypes.c_int)。通过提供此信息,ctypes 可以执行类型检查以及 Python 和 C 类型之间的自动转换。

处理指针和复杂数据类型

ctypes 支持指针、结构体、数组和其他复杂数据类型,允许您在 C 中处理更复杂的数据结构。以下是使用 ctypes 和指针的示例。

我们将此代码编译为共享库,并从 Python 调用 square 函数。

输出

Square: 25

在此示例中,我们使用 ctypes.POINTER() 定义了一个指向整数的指针(int *)。我们使用 ctypes.byref() 将指针传递给 square 函数,该函数接受一个 Python 对象并返回指向它的指针。这演示了 ctypes 如何轻松处理 C 中的指针和复杂数据类型。

使用 cffi 进行高级交互

与 ctypes 相比,cffi 是一个更高级别的库,用于从 Python 调用 C 函数。它提供了更 Pythonic 的 API,并为处理复杂数据类型和内存管理提供了更好的支持。cffi 在处理更高级的 C 交互任务时特别有用,并且在性能和内存效率至关重要的项目中得到了广泛使用。

安装

您可以通过 pip,Python 的包管理器安装 cffi。

在运行上述命令之前,请确保您的系统已安装并配置了 pip。

开始

让我们探讨使用 cffi 从 Python 调用 C 函数的基本用法。我们将从一个简单的示例开始,该示例调用一个添加两个整数的 C 函数。

输出

Result of expansion: 8

这段代码片段演示了使用 cffi 的基本工作流程。

  • 我们创建一个 FFI 对象,它充当与 C 代码的接口。
  • 我们使用 ffi.cdef() 方法定义 C 函数的签名,以 C 语法指定函数的返回类型和参数类型。
  • 我们使用 ffi.dlopen() 方法加载包含 C 函数的共享库。
  • 最后,我们像调用 Python 函数一样调用 C 函数。

处理指针和内存分配

cffi 提供了方便的函数来分配内存和处理 C 中的指针。以下是使用 cffi 处理指针和内存分配的示例。

输出

Square: 5

在此示例中,我们使用 ffi.new() 方法为整数(int *)分配内存,并用值 5 初始化它。然后,我们将指针传递给 square 函数,该函数修改了指针指向的值。最后,我们打印结果,展示了 cffi 在 C 中处理指针和内存管理的能力。

用例

将 C 代码与 Python 集成在许多场景下都很有用,包括但不限于:

  1. 科学计算和数值分析:许多流行的数学库,如 NumPy 和 SciPy,其性能关键部分是用 C 实现的。通过从 Python 调用这些函数,科学家和工程师可以高效地执行复杂的计算。
  2. 图形和多媒体应用程序:像 OpenGL 这样的图形库和像 FFmpeg 这样的多媒体框架通常提供 C API,用于高效地处理图形和媒体数据。通过与这些库从 Python 交互,开发人员可以创建复杂的图形应用程序和多媒体工具。
  3. 系统编程:在需要底层访问系统资源的系统编程中,从 Python 调用 C 函数允许开发人员与底层操作系统和硬件组件进行交互。
  4. 游戏开发:游戏引擎和图形库通常提供 C API 来渲染图形、处理输入设备和管理游戏资产。通过从 Python 调用这些函数,游戏开发人员可以构建高性能游戏,同时受益于 Python 的高级脚本功能。
  5. 嵌入式系统开发:在资源有限且性能至关重要的嵌入式系统领域,从 Python 调用 C 函数使开发人员能够为嵌入式设备编写高效的代码,同时仍然受益于 Python 的易于开发和快速原型制作能力。