c语言执行python

99ANYc3cd6
预计阅读时长 25 分钟
位置: 首页 C语言 正文

核心思想是使用 Python/C API,这是一个由 Python 解释器提供的 C 语言接口,允许 C 程序调用 Python 函数、操作 Python 对象,并嵌入 Python 解释器。

c语言执行python
(图片来源网络,侵删)

下面我将从简单到复杂,分步讲解如何实现。


直接执行 Python 字符串代码 (最简单)

这种方法适用于执行一小段动态的 Python 代码。

核心步骤:

  1. 初始化 Python 解释器 (Py_Initialize)。
  2. 执行 Python 代码 (PyRun_SimpleString)。
  3. 关闭 Python 解释器 (Py_Finalize)。

代码示例 (C)

// my_c_program.c
#include <Python.h>
int main(int argc, char *argv[]) {
    // 1. 初始化 Python 解释器
    Py_Initialize();
    if (!Py_IsInitialized()) {
        fprintf(stderr, "Error: Failed to initialize the Python interpreter.\n");
        return 1;
    }
    // 2. 执行 Python 代码字符串
    //    - 可以执行简单的表达式
    PyRun_SimpleString("print('Hello from Python!')");
    //    - 也可以执行多行代码块
    PyRun_SimpleString("a = 10\n"
                       "b = 20\n"
                       "print(f'The sum of a and b is: {a + b}')");
    // 3. 关闭 Python 解释器,释放资源
    Py_Finalize();
    printf("C program finished.\n");
    return 0;
}

如何编译和运行?

你需要先安装 Python 开发头文件和库,在 Linux 上,通常使用 python3-devpython3-devel 包。

安装依赖 (以 Ubuntu/Debian 为例)

c语言执行python
(图片来源网络,侵删)
sudo apt-get update
sudo apt-get install python3-dev

编译 C 代码 你需要告诉编译器去哪里找 Python 的头文件和库文件。python3-config 命令可以方便地获取这些信息。

# 使用 python3-config 自动获取编译参数
gcc my_c_program.c -o my_c_program $(python3-config --cflags --ldflags)
  • --cflags: 获取编译所需的 C 编译器标志(如头文件路径 -I)。
  • --ldflags: 获取链接所需的库文件标志(如库路径 -L 和库名 -lpython3.x)。

运行

./my_c_program

预期输出:

Hello from Python!
The sum of a and b is: 30
C program finished.

执行一个 Python 文件 (更实用)

这种方法更常见,即 C 程序调用一个外部的 .py 脚本。

核心步骤:

  1. 初始化 Python 解释器 (Py_Initialize)。
  2. 将 Python 脚本所在的目录添加到 Python 的模块搜索路径 (sys.path) 中,这样 Python 才能找到你的脚本。
  3. 使用 PyImport_ImportModule 导入 Python 模块(即你的 .py 文件)。
  4. 获取模块中的函数 (PyObject_GetAttrString)。
  5. 调用函数 (PyObject_CallObject)。
  6. 处理返回值和异常。
  7. 关闭 Python 解释器 (Py_Finalize)。

代码示例 (C)

假设我们有以下 Python 文件:

my_python_script.py

# my_python_script.py
import sys
def greet(name):
    """Prints a greeting and returns a message."""
    print(f"Python: Hello, {name}!")
    return f"Greeting sent to {name}."
def add_numbers(a, b):
    """Adds two numbers and returns the result."""
    if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
        raise TypeError("Both arguments must be numbers.")
    return a + b

main.c

// main.c
#include <Python.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
    Py_Initialize();
    if (!Py_IsInitialized()) {
        fprintf(stderr, "Error: Failed to initialize the Python interpreter.\n");
        return 1;
    }
    // 将当前目录添加到 Python 的模块搜索路径
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('.')");
    // 导入 Python 模块
    PyObject* pName = PyUnicode_DecodeFSDefault("my_python_script"); // 模块名
    PyObject* pModule = PyImport_Import(pName);
    Py_DECREF(pName);
    if (pModule != NULL) {
        // --- 调用 greet 函数 ---
        PyObject* pFunc = PyObject_GetAttrString(pModule, "greet");
        if (pFunc && PyCallable_Check(pFunc)) {
            PyObject* pArgs = PyTuple_New(1);
            PyTuple_SetItem(pArgs, 0, PyUnicode_FromString("C World")); // 参数 "C World"
            PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                // 处理 greet 的返回值
                printf("C: Received return value from greet: %s\n", PyUnicode_AsUTF8(pValue));
                Py_DECREF(pValue);
            } else {
                PyErr_Print(); // 打印 Python 异常
            }
        } else {
            if (PyErr_Occurred()) PyErr_Print();
            fprintf(stderr, "Cannot find function greet\n");
        }
        Py_XDECREF(pFunc);
        // --- 调用 add_numbers 函数 ---
        PyObject* pFuncAdd = PyObject_GetAttrString(pModule, "add_numbers");
        if (pFuncAdd && PyCallable_Check(pFuncAdd)) {
            PyObject* pArgsAdd = PyTuple_New(2);
            PyTuple_SetItem(pArgsAdd, 0, PyLong_FromLong(15)); // 参数 15
            PyTuple_SetItem(pArgsAdd, 1, PyLong_FromLong(27)); // 参数 27
            PyObject* pValueAdd = PyObject_CallObject(pFuncAdd, pArgsAdd);
            Py_DECREF(pArgsAdd);
            if (pValueAdd != NULL) {
                // 处理 add_numbers 的返回值
                long result = PyLong_AsLong(pValueAdd);
                printf("C: Received return value from add_numbers: %ld\n", result);
                Py_DECREF(pValueAdd);
            } else {
                PyErr_Print(); // Python 函数内部可能会抛出异常
            }
        } else {
            if (PyErr_Occurred()) PyErr_Print();
            fprintf(stderr, "Cannot find function add_numbers\n");
        }
        Py_XDECREF(pFuncAdd);
    } else {
        PyErr_Print();
        fprintf(stderr, "Failed to load module my_python_script\n");
    }
    Py_XDECREF(pModule);
    Py_Finalize();
    return 0;
}

如何编译和运行?

编译命令和之前一样:

gcc main.c -o my_c_app $(python3-config --cflags --ldflags)

运行

./my_c_app

预期输出:

Python: Hello, C World!
C: Received return value from greet: Greeting sent to C World.
C: Received return value from add_numbers: 42

关键点与注意事项

  1. 内存管理: Python/C API 中的对象都是通过引用计数管理的,当你创建一个新对象(如 PyUnicode_FromString)或获取一个已有对象的引用(如 PyObject_GetAttrString)时,它的引用计数会增加,当你使用完这个对象后,必须调用 Py_DECREF 来减少引用计数,当计数归零时,内存会被自动释放,忘记 Py_DECREF 会导致内存泄漏。Py_XDECREF 是一个更安全的版本,它会在对象不为 NULL 时才进行 DecRef

  2. 错误处理: Python 函数可能会抛出异常,在 C 端调用后,必须检查返回值是否为 NULL,如果为 NULL,通常意味着发生了异常,此时应调用 PyErr_Print() 来打印 Python 级别的错误堆栈信息,否则错误信息会丢失。

  3. 数据类型转换: C 语言和 Python 的数据类型是不同的,API 提供了转换函数:

    • C int -> Python int: PyLong_FromLong()
    • Python int -> C long: PyLong_AsLong()
    • C char* -> Python str: PyUnicode_FromString()
    • Python str -> C char*: PyUnicode_AsUTF8()
    • 创建 Python 元组: PyTuple_New(), PyTuple_SetItem()
  4. GIL (全局解释器锁): 当你嵌入 Python 解释器时,默认情况下你会获得 GIL,这意味着在调用 Python 代码时,你的 C 线程是独占解释器的,如果你的 C 程序是多线程的,并且希望其他线程也能同时执行 Python 代码,你需要使用 PyGILState_Ensure()PyGILState_Release() 来管理 GIL 的状态。

  5. 性能: 在 C 和 Python 之间频繁地传递数据(尤其是在循环中)会有性能开销,因为涉及类型转换和 API 调用,对于性能敏感的场景,最好将大量的计算逻辑放在 Python 端,C 端只负责启动和最终结果的获取。

方法 适用场景 优点 缺点
PyRun_SimpleString 执行少量、固定的 Python 代码。 简单直接,无需外部文件。 代码难以管理,无法复用,功能有限。
Python/C API 调用模块 C 程序需要调用一个或多个独立的 Python 脚本/模块。 功能强大,代码结构清晰,易于维护和复用 Python 代码。 实现较复杂,需要处理内存管理和错误检查。

对于绝大多数应用场景,方法二(调用 Python 模块) 是更推荐和更实用的方案,它将 C 和 Python 的职责清晰地分开,使得代码更易于维护和扩展。

-- 展开阅读全文 --
头像
织梦无法生成文章页
« 上一篇 今天
dede联动模板免费在哪里下载?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]