C语言调用python无参函数-笔记

这篇文章详细描述了如何在C语言程序中嵌入和使用Python解释器,通过PythonCAPI进行模块导入、函数调用等操作,并展示了如何处理可能出现的错误和资源管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         使用 C 语言编写的程序,从 C 代码中嵌入和使用 Python 解释器,涉及到了多个 Python C API 的函数。

从 C 代码中嵌入和使用 Python 解释器通常涉及以下步骤:

1. 初始化 Python 解释器

在开始使用 Python C API 之前,你需要初始化 Python 解释器。这通常通过调用 Py_Initialize()PyImport_Initialize() 函数来完成。

2. 设置 Python 路径

为了确保 Python 能够找到并导入所需的模块,你可能需要修改 sys.path。这可以通过导入 sys 模块并使用 PyObject_GetAttrString()PyList_Append() 函数来完成。

3. 导入 Python 模块

使用 PyImport_ImportModule() 函数导入你需要的 Python 模块。这个函数接受模块名称作为参数,并返回一个模块对象。

4. 获取 Python 函数或变量

一旦你有了模块对象,你可以使用 PyObject_GetAttrString() 函数来获取模块中的函数或变量。这将返回一个对象,你可以检查它是否是可调用的,如果是函数,你可以直接调用它。

5. 调用 Python 函数

使用 PyObject_CallObject()PyObject_CallFunction() 等函数调用 Python 函数。你需要传递函数对象和参数列表(如果有的话)。这将执行 Python 函数并返回一个结果对象。

6. 处理返回值

如果 Python 函数有返回值,你需要检查返回的对象是否有效,并根据需要处理它。这可能包括获取对象的值、转换类型或直接打印输出。

7. 清理资源

在 C 代码中使用 Python 对象后,你需要适当地清理资源。这包括使用 Py_DECREF() 函数减少对象的引用计数,以避免内存泄漏。

8. 关闭 Python 解释器

当你完成所有操作后,调用 Py_Finalize() 函数来关闭 Python 解释器并清理所有资源。

Python C API 的函数:

  1. Py_Initialize():

    • 初始化 Python 解释器。
    • 必须在调用其他 Python C API 函数之前执行。
    • 这会设置 Python 的内部状态并准备解释器运行。
  2. PyImport_ImportModule(const char *name):

    • 导入指定名称的 Python 模块。
    • 返回模块对象,如果导入失败则返回 NULL。
  3. PyObject_GetAttrString(PyObject *o, const char *attr):

    • 从给定对象(通常是模块对象)中获取指定名称的属性(属性名以字符串形式给出)。
    • 返回属性值对象,如果失败则返回 NULL。
  4. PyList_Append(PyObject *list, PyObject *object):

    • 将一个新对象添加到列表的末尾。
    • 用于修改 sys.path,以便 Python 能够找到指定的模块。
  5. PyErr_Print():

    • 打印当前 Python 错误到标准错误输出。
    • 用于调试和错误处理。
  6. PyCallable_Check(PyObject *p):

    • 检查给定对象是否是一个可调用对象(即函数或方法)。
    • 如果对象是可调用的,返回 1;否则返回 0。
  7. PyObject_CallObject(PyObject *callable, PyObject *args):

    • 调用一个可调用对象,如函数或方法。
    • args 参数是传递给函数的参数列表,如果函数不接受参数,则传递 NULL。
  8. Py_DECREF(PyObject *obj):

    • 减少对象的引用计数。
    • 当引用计数降至 0 时,Python 将释放对象占用的内存。
  9. Py_Finalize():

    • 关闭 Python 解释器并释放所有资源。
    • 通常在程序结束时调用。

         以下代码演示了如何从 C 代码中嵌入和使用 Python 解释器。代码的主要功能是导入一个名为 "nopara" 的 Python 模块,并调用其中的 say_funny 函数。

#include <Python.h>
int main()
{
	Py_Initialize(); // 初始化

	PyObject* sys = PyImport_ImportModule("sys"); // 导入sys模块
	PyObject* path = PyObject_GetAttrString(sys, "path"); // 获取sys.path对象
	PyList_Append(path, PyUnicode_FromString(".")); // 将当前路径添加到sys.path中

	PyObject* pModule = PyImport_ImportModule("nopara"); // 导入nopara模块
	if (!pModule) // 若导入失败
	{
		PyErr_Print(); // 打印错误信息
		printf("ERROR: failed to load nopara.py\n"); // 输出错误提示
		return 1; // 返回1,表示出错
	}

	PyObject* pFunc = PyObject_GetAttrString(pModule, "say_funny"); // 获取say_funny函数对象
	if (!pFunc || !PyCallable_Check(pFunc)) // 若函数对象不存在或不可调用
	{
		PyErr_Print(); // 打印错误信息
		printf("ERROR: function say_funny not found or not callable\n"); // 输出错误提示
		return 1; // 返回1,表示出错
	}

	PyObject* pValue = PyObject_CallObject(pFunc, NULL); // 调用say_funny函数并获取返回值
	if (!pValue) // 若返回值为空
	{
		PyErr_Print(); // 打印错误信息
		printf("ERROR: function call failed\n"); // 输出错误提示
		return 1; // 返回1,表示出错
	}

	Py_DECREF(pValue); // 释放返回值对象
	Py_DECREF(pFunc); // 释放函数对象
	Py_DECREF(pModule); // 释放模块对象

	Py_Finalize(); // 关闭Python解释器
	return 0; // 返回0,表示运行成功
}

下面是对这段代码的详细解释:

包含 Python 头文件

#include <Python.h>

这行代码包含了 Python 的 C 接口头文件,它提供了所有必要的宏、类型定义和函数原型,以便在 C 程序中使用 Python 功能。

主函数

int main() {

这是 C 程序的主函数,程序的执行从这里开始。

初始化 Python 解释器

Py_Initialize();

这行代码初始化 Python 解释器。这是使用 Python C API 的第一步,它为解释器设置了一个基本的状态。

导入 sys 模块并修改 sys.path

PyObject* sys = PyImport_ImportModule("sys"); PyObject* path = PyObject_GetAttrString(sys, "path"); PyList_Append(path, PyUnicode_FromString("."));

这几行代码做了以下操作:

  • 导入 Python 的 sys 模块并将其对象存储在 sys 变量中。
  • 从 sys 模块中获取 path 属性,这是一个列表,包含了 Python 模块搜索的路径。
  • 将当前目录(.)添加到 sys.path 中,这样 Python 就可以在当前目录中搜索模块了。

导入 nopara 模块

PyObject* pModule = PyImport_ImportModule("nopara");

这行代码尝试导入名为 "nopara" 的 Python 模块。如果导入成功,模块对象将被存储在 pModule 变量中。

检查模块导入和函数是否存在

if (!pModule) { PyErr_Print(); printf("ERROR: failed to load nopara.py\n"); return 1; }

如果模块导入失败,这行代码会打印错误信息并返回 1,表示程序执行出错。

获取并检查 say_funny 函数

PyObject* pFunc = PyObject_GetAttrString(pModule, "say_funny"); if (!pFunc || !PyCallable_Check(pFunc)) { PyErr_Print(); printf("ERROR: function say_funny not found or not callable\n"); return 1; }

这几行代码尝试从 nopara 模块中获取名为 say_funny 的函数对象,并检查该对象是否存在以及是否可调用。如果不满足条件,会打印错误信息并返回 1。

调用 say_funny 函数并获取返回值

PyObject* pValue = PyObject_CallObject(pFunc, NULL);

这行代码调用 say_funny 函数,NULL 表示没有传递任何参数。函数的返回值将被存储在 pValue 变量中。

检查函数调用是否成功

if (!pValue) { PyErr_Print(); printf("ERROR: function call failed\n"); return 1; }

如果函数调用失败,这行代码会打印错误信息并返回 1。

清理资源

Py_DECREF(pValue); Py_DECREF(pFunc); Py_DECREF(pModule);

这几行代码递减了 pValuepFuncpModule 对象的引用计数。这是 Python C API 的标准做法,用于释放不再使用的资源。

关闭 Python 解释器

Py_Finalize();

这行代码关闭 Python 解释器,释放所有剩余的资源。

注意事项:在编译 C 代码时链接了 Python 库,通常通过在编译命令中添加 -lpythonX.Y(其中 X.Y 是 Python 版本号)来实现。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值