一、搭建编译环境
- 终端查询系统及软件版本
- dpkg -l 列出所有已安装的软件包
二、C语言中调用Python
- 使用 GCC编译并链接 Python 3.10 的共享库
- 如何在C中获取和修改 sys.path
三、C语言调用无参python函数
四、C语言调用有参python函数
一、搭建编译环境
通过C语言调用Python代码,需要先安装libpython3的 dev依赖库(不同的ubuntu版本下,python版本可
能会有差异, 比如ubuntu 22.04里是libpython3.10-dev)。
libpython3 通常指的是 Python 3 的共享库文件(shared library)。在 Linux 系统上,这个库文件的名称可能会有所不同,通常是以 libpython3.x.so 的形式命名,其中 x 是 Python 3 的主版本号。
如果你希望在编译时链接 Python 3 的共享库,你需要确保已经安装了对应版本的 Python 3 开发包。这通常包括了 Python 3 的头文件和共享库文件。你可以使用系统的包管理工具来安装这些开发包。
以下是在一些流行的 Linux 发行版上安装 Python 3 开发包的示例:
-
在 Ubuntu 或者 Debian 上:
sudo apt-get update sudo apt-get install python3-dev -
在 Fedora 上:
sudo dnf install python3-devel -
在 CentOS 或者 RHEL 上:
sudo yum install python3-devel
一旦你安装了 Python 3 的开发包,你就应该能够在系统中找到 libpython3.x.so 文件,其中 x 是你安装的 Python 3 的主版本号。这个文件通常位于 /usr/lib/ 或 /usr/lib64/ 目录下。
请注意,如果你在编译时使用 gcc 或其他编译器,你可能需要使用 -lpython3.x 标志来链接 Python 3 的共享库。例如:
gcc -o my_program my_program.c -I/usr/include/python3.x -lpython3.x
请替换 3.x 为你所使用的 Python 3 的版本号。
终端查询系统及软件版本
这些命令用于检查内核版本、系统信息、Python版本、GCC版本和Linux发行版信息。
以下是每个命令的简要说明:
-
cat /proc/version: 显示Linux内核版本和系统信息。 -
cat /etc/issue: 打印/etc/issue文件的内容,该文件通常包含有关Linux发行版的信息。 -
uname -a: 打印系统信息,如内核名称、网络节点主机名、内核发布版本、内核版本、机器硬件名称和处理器类型。 -
python --version: 显示Python解释器的版本。 -
gcc -v: 显示GCC(GNU编译器集合)的版本。 -
lsb_release -a: 打印LSB(Linux标准基础)和特定于发行版的信息。LSB是一组标准,旨在增加Linux发行版之间的兼容性。
您可以在终端中运行这些命令来查看有关您的Linux系统的相应信息。 请记住,这些命令的可用性和输出可能会有所不同,具体取决于您的特定 Linux 发行版和系统配置。
dpkg -l 列出所有已安装的软件包
这个命令用于列出已安装的与 Python 3 开发相关的库。具体来说,它使用 dpkg -l 列出所有已安装的软件包,然后使用 grep 过滤出包含 “libpython3” 和 “dev” 的行。
在 Ubuntu 或 Debian 等基于 Debian 的系统上,你可以运行以下命令:
dpkg -l | grep libpython3 | grep dev
如果你已经安装了 Python 3 的开发包,命令的输出应该包含与 Python 3 相关的开发库。如果没有安装,输出将为空。
请注意,这是一种查找已安装软件包的方法,确保你使用了适用于你的 Linux 发行版的包管理工具。
二、C语言中调用Python
在C语言中调用Python代码通常涉及到使用Python的C API。Python提供了一组C API,允许在C中调用Python函数、处理Python对象等。
下面是一个简单的例子,展示了如何在C中调用Python代码:
-
Python脚本: 编写一个简单的Python脚本,保存为
example.py。# example.py def add_numbers(a, b):return a + b -
C代码: 编写一个C程序
invoke.c,调用Python中的函数。#include <Python.h>int main() {// 初始化Python解释器Py_Initialize();// 导入Python模块PyObject* pModule = PyImport_ImportModule("example");if (pModule != NULL) {// 获取Python函数对象PyObject* pFunction = PyObject_GetAttrString(pModule, "add_numbers");if (pFunction != NULL) {// 调用Python函数PyObject* pArgs = PyTuple_Pack(2, PyLong_FromLong(3), PyLong_FromLong(5));PyObject* pResult = PyObject_CallObject(pFunction, pArgs);if (pResult != NULL) {// 处理Python函数的返回值printf("Result: %ld\n", PyLong_AsLong(pResult));Py_DECREF(pResult);} else {PyErr_Print(); // 打印Python错误信息}Py_DECREF(pFunction);Py_DECREF(pArgs);} else {PyErr_Print();}Py_DECREF(pModule);} else {PyErr_Print();}// 关闭Python解释器Py_Finalize();return 0; } -
编译: 使用合适的编译器将C代码编译成可执行文件。
gcc invoke.c -o invoke -I /usr/include/python3.x -l python3.x gcc -o simpledemo simpledemo.c -I /usr/include/python3.x -l python3.x请确保替换
3.x为你所使用的Python版本号。 -
运行: 运行生成的可执行文件。
./invoke
这个例子中,C程序初始化了Python解释器,导入了一个Python模块,获取了Python模块中的一个函数,调用了这个函数,并处理了Python函数的返回值。这只是一个简单的例子,实际应用可能涉及更复杂的交互和数据传递。
使用 GCC编译并链接 Python 3.10 的共享库
这段命令和代码的作用是:
-
ls /usr/include/python:列出/usr/include/python目录下的内容。通常,Python 的头文件(.h文件)会在这个目录下。 -
gcc invoke.c -o invoke -I /usr/include/python3.10 -lpython3.10:使用 GCC 编译invoke.c文件,并链接 Python 3.10 的共享库。其中,-I选项指定了头文件所在的目录,-lpython3.10用于链接 Python 3.10 的共享库。编译后的可执行文件名为invoke。 -
./invoke:运行编译生成的invoke可执行文件。
这段命令和代码的目的可能是在 invoke.c 文件中调用了 Python 3.10 的一些函数,并通过 GCC 编译器将其与 Python 3.10 的共享库链接起来。在运行 invoke 可执行文件时,它可能会执行 invoke.c 中的 Python 相关代码。
请确保你的系统中已经安装了 Python 3.10 的开发包,以及 GCC 编译器。如果没有安装,你可以使用系统的包管理工具进行安装。
如何在C中获取和修改 sys.path
sys.path列表里储存了python默认库搜索路径
在Python中,sys.path 是一个包含模块搜索路径的列表。当你导入一个模块时,Python会在这些路径中查找模块文件。
在C语言中,你可以通过Python/C API来获取和操作 sys.path。以下是一个简单的例子,展示了如何在C中获取和修改 sys.path:
#include <Python.h>int main() {// 初始化Python解释器Py_Initialize();// 获取sys.path对象PyObject* sysPath = PySys_GetObject("path");// 打印原始sys.pathprintf("Original sys.path:\n");for (Py_ssize_t i = 0; i < PyList_Size(sysPath); ++i) {PyObject* pathItem = PyList_GetItem(sysPath, i);const char* pathStr = PyUnicode_AsUTF8(pathItem);printf("%s\n", pathStr);}// 添加新路径到sys.pathPyList_Append(sysPath, PyUnicode_DecodeFSDefault("/path/to/your/directory"));// 打印修改后的sys.pathprintf("\nModified sys.path:\n");for (Py_ssize_t i = 0; i < PyList_Size(sysPath); ++i) {PyObject* pathItem = PyList_GetItem(sysPath, i);const char* pathStr = PyUnicode_AsUTF8(pathItem);printf("%s\n", pathStr);}// 关闭Python解释器Py_Finalize();return 0;
}
这个例子中,我们使用 PySys_GetObject("path") 获取了 sys.path 对象,并使用 PyList_Append 将新的路径添加到列表中。在实际应用中,你可能会根据你的需求添加不同的路径。请注意,在真实的应用中,你可能需要更加谨慎地处理路径,以确保不会破坏系统的正常运行。
#!/usr/bin/python3import sysprint(sys.path)

三、C语言调用无参python函数
C语言中调用无参Python函数的步骤
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)
获取sys.path对象,并利用int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,
以便加载当前的Python模块(Python文件即python模块)。
4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取Python函数对象,并检查是否可调用。
6、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用Python函数,并获取返回值。
7、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
8、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
3.10 Documentation » Python/C API 参考手册 » 工具 » 导入模块
#include <Python.h>int main() {// 1. 初始化Python解释器Py_Initialize();// 2. 获取sys.path对象,并将当前路径添加到sys.path中PyObject* sysPath = PySys_GetObject("path");PyList_Append(sysPath, PyUnicode_DecodeFSDefault("."));// 3. 导入Python模块PyObject* pModule = PyImport_ImportModule("your_module_name");if (pModule != NULL) {// 4. 获取Python函数对象PyObject* pFunction = PyObject_GetAttrString(pModule, "your_function_name");if (pFunction != NULL && PyCallable_Check(pFunction)) {// 5. 调用Python函数(这里是无参数调用)PyObject* pArgs = PyTuple_New(0); // 传递空参数元组PyObject* pResult = PyObject_CallObject(pFunction, pArgs);if (pResult != NULL) {// 6. 处理返回值,可以在这里进行你的操作Py_DECREF(pResult);} else {// 打印Python错误信息PyErr_Print();}// 释放Python函数对象和参数Py_DECREF(pFunction);Py_DECREF(pArgs);} else {// 打印Python错误信息PyErr_Print();}// 释放Python模块对象Py_DECREF(pModule);} else {// 打印Python错误信息PyErr_Print();}// 7. 关闭Python解释器Py_Finalize();return 0;
}
请确保替换 “your_module_name” 和 “your_function_name” 为你实际使用的Python模块和函数的名称。
这段代码演示了如何在C中调用一个无参数的Python函数。
根据上面的流程写出的示例代码如下:
# nopara.py文件
# !/usr/bin/python3def say_funny():print('funny')
#include <Python.h>int main()
{// 1. 初始化Python解释器Py_Initialize();// 2. 获取sys.path对象,并将当前路径添加到sys.path中PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString(".")); // PyUnicode_FromString将c字符串转换成Python字符串// 3. 导入Python模块PyObject *pModule = PyImport_ImportModule("nopara");if (!pModule) {PyErr_Print();printf("ERROR: failed to load nopara.py\n");return 1;}// 4. 获取Python函数对象say_funnyPyObject *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;}// 5. 调用Python函数,调用say_funny函数并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, NULL);if (!pValue ) {PyErr_Print();printf("ERROR: function call failed\n");return 1;}// 释放所有引用的Python对象Py_DECREF(pValue);Py_DECREF(pFunc);Py_DECREF(pModule);// 关闭Python解释器Py_Finalize();return 0;
}
编译和运行这个程序,可以使用以下命令(假设使用的是gcc编译器和Python 3.10版本):
gcc -o nopara nopara.c -I /usr/include/python3.10/ -l python3.10
./nopara

四、C语言调用有参python函数
C语言中调用有参Python函数的步骤
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)
获取sys.path对象,并利用int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,
以便加载当前的Python模块(Python文件即python模块)。
4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取Python函数对象,并检查是否可调用。
6、使用PyObject *Py_BuildValue(const char *format, …)函数将C类型的数据结构转换成Python对象,
作为Python函数的参数,没有参数不需要调用
7、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用Python函数,并获取返回值。
8、使用int PyArg_Parse(PyObject *args, const char *format, …)函数将返回值转换为C类型,
并检查是否有错误,没有返回值时不需要调用。
9、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
10、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
3.12.0 Documentation » Python/C API 参考手册 » 工具 » 导入模块
在C语言中调用有参Python函数的步骤。这里是整理后的步骤:
#include <Python.h>int main() {// 1. 初始化Python解释器Py_Initialize();// 2. 获取sys.path对象,并将当前路径添加到sys.path中PyObject* sysPath = PySys_GetObject("path");PyList_Append(sysPath, PyUnicode_DecodeFSDefault("."));// 3. 导入Python模块PyObject* pModule = PyImport_ImportModule("your_module_name");if (pModule != NULL) {// 4. 获取Python函数对象PyObject* pFunction = PyObject_GetAttrString(pModule, "your_function_name");if (pFunction != NULL && PyCallable_Check(pFunction)) {// 5. 构建Python函数的参数PyObject* pArgs = Py_BuildValue("(ii)", 42, 17); // 传递两个整数参数if (pArgs != NULL) {// 6. 调用Python函数PyObject* pResult = PyObject_CallObject(pFunction, pArgs);if (pResult != NULL) {// 7. 处理返回值,可以在这里进行你的操作// 8. 将返回值转换为C类型int resultValue;if (PyArg_Parse(pResult, "i", &resultValue)) {// 处理resultValue} else {// 转换错误PyErr_Print();}Py_DECREF(pResult);} else {// 打印Python错误信息PyErr_Print();}// 释放Python函数对象和参数Py_DECREF(pFunction);Py_DECREF(pArgs);} else {// 参数构建错误PyErr_Print();}} else {// 打印Python错误信息PyErr_Print();}// 释放Python模块对象Py_DECREF(pModule);} else {// 打印Python错误信息PyErr_Print();}// 9. 关闭Python解释器Py_Finalize();return 0;
}
请确保替换 “your_module_name” 和 “your_function_name” 为你实际使用的Python模块和函数的名称,并根据你的函数参数类型调整 Py_BuildValue 和 PyArg_Parse 的格式字符串。
第六步传参
6、使用PyObject *Py_BuildValue(const char *format, …)函数创建一个Python元组或者对象,
作为Python函数的参数,没有参数势不需要调用
这里要注意的是,Py_BuildValue的第一个参数是类型转换:C对应的Python的数据类型转换对应的格式如下:
Py_BuildValue 函数用于将C中的数据转换为Python对象。它的第一个参数是一个格式字符串,指定了如何构建Python对象。以下是一些常见的格式码及其对应的C数据类型:
's':字符串- C类型:
const char*
- C类型:
'i':整数- C类型:
int
- C类型:
'l':长整数- C类型:
long
- C类型:
'f':浮点数- C类型:
double
- C类型:
'd':双精度浮点数- C类型:
double
- C类型:
'O':任意Python对象- C类型:
PyObject*
- C类型:
例如,如果你想构建一个Python元组,其中包含一个整数和一个字符串,可以使用以下格式字符串:
PyObject* result = Py_BuildValue("(is)", 42, "hello");
这将构建一个包含一个整数和一个字符串的元组。第一个参数 42 对应 i 表示整数,第二个参数 "hello" 对应 s 表示字符串。
以下是一些常用的格式码:
'()':元组'[]':列表'{}':字典's':字符串'i':整数'l':长整数'f':浮点数'd':双精度浮点数'O':任意Python对象
你可以根据实际需要选择合适的格式码,并使用相应的C数据类型提供参数。
确保在使用 Py_BuildValue 时,提供的参数数量和格式码匹配,以避免运行时错误。
| 格式码 | C数据类型 | 示例 |
|---|---|---|
| 's' | const char* | Py_BuildValue("s", "hello") |
| 'i' | int | Py_BuildValue("i", 42) |
| 'l' | long | Py_BuildValue("l", 123456789L) |
| 'f' | float | Py_BuildValue("f", 3.14f) |
| 'd' | double | Py_BuildValue("d", 2.71828) |
| 'O' | PyObject* | Py_BuildValue("O", somePyObject) |
| '()' | Tuple | Py_BuildValue("(is)", 42, "hello") |
| '[]' | List | Py_BuildValue("[i]", 1) |
| '{}' | Dictionary | Py_BuildValue("{s:i}", "key", 42) |
PyArg_Parse 是 Python/C API 中用于将 Python 对象参数转换为 C 数据类型的函数。它通常用于解析从 Python 调用的函数传递的参数。以下是 PyArg_Parse 的基本用法:
int PyArg_Parse(PyObject *args, const char *format, ...);
args: 一个包含传递给函数的参数的元组。format: 包含格式化指令的字符串,指定了如何解析参数。...: 用于接收解析后的参数值的变量。
PyArg_Parse 的格式化指令可以是以下之一:
'i': 整数'f': 浮点数's': 字符串'O': 任意对象
以下是一些示例:
-
解析一个整数参数:
int value; if (!PyArg_Parse(args, "i", &value)) {// 处理解析错误return NULL; } -
解析一个浮点数参数:
double value; if (!PyArg_Parse(args, "f", &value)) {// 处理解析错误return NULL; } -
解析一个字符串参数:
const char *text; if (!PyArg_Parse(args, "s", &text)) {// 处理解析错误return NULL; } -
解析一个任意对象参数:
PyObject *obj; if (!PyArg_Parse(args, "O", &obj)) {// 处理解析错误return NULL; }
PyArg_Parse 还支持更复杂的格式化字符串,允许你指定多个参数以及它们的类型。例如:
int x, y;
if (!PyArg_Parse(args, "(ii)", &x, &y)) {// 处理解析错误return NULL;
}
在这个例子中,期望参数是一个包含两个整数的元组。
注意:PyArg_Parse 在解析参数时会自动增加引用计数,但在解析失败时需要手动处理错误。如果解析失败,它会返回 0,并且你应该在错误处理中返回 NULL 或适当的错误信息。
示例代码如下:
# para.py文件
# !/usr/bin/python3def say_funny(category):print(category)return category
#if 0
1、包含Python.h头文件,以便使用Python API。2、使用void Py_Initialize()初始化Python解释器,3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)
获取sys.path对象,并利用int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,
以便加载当前的Python模块(Python文件即python模块)。4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否有错误。5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取Python函数对象,并检查是否可调用。6、使用PyObject *Py_BuildValue(const char *format, ...)函数将C类型的数据结构转换成Python对象,
作为Python函数的参数,没有参数不需要调用。7、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用Python函数,并获取返回值。8、使用int PyArg_Parse(PyObject *args, const char *format, ...)函数将返回值转换为C类型,
并检查是否有错误,没有返回值时不需要调用。9、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。10、结束时调用void Py_Finalize()函数关闭Python解释器。相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
https://docs.python.org/zh-cn/3/c-api/import.html
#endif#include <Python.h>int main()
{// 初始化Python解释器Py_Initialize();// 获取sys.path对象,并将当前路径添加到sys.path中PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString(".")); // PyUnicode_FromString将c字符串转换成Python字符串// 导入Python模块PyObject *pModule = PyImport_ImportModule("para");if (!pModule ) {PyErr_Print();printf("ERROR: failed to load para.py\n");}// 获取Python函数对象say_funnyPyObject *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;}//创建一个字符串作为参数char *category = "comedy";PyObject *pArgs = Py_BuildValue("(s)", category);// 调用Python函数,调用say_funny函数并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, pArgs);if (!pValue) {PyErr_Print();printf("ERROR: function call failed\n");}//将返回值转换为C类型char *result = NULL;if (!PyArg_Parse(pValue, "s", &result)) {PyErr_Print();printf("Error: parse failed\n");}//打印返回值printf("pValue=%s\n", result); // 释放所有引用的Python对象Py_DECREF(pValue);Py_DECREF(pFunc);Py_DECREF(pModule);// 关闭Python解释器Py_Finalize();return 0;
}
编译和运行这个程序,可以使用以下命令(假设使用的是gcc编译器和Python 3.10版本):
gcc para.c -o para -I /usr/include/python3.10 -l python3.10
./para

