10.【Orangepi Zero2】C语言调用Python

Python基础

环境搭建:(备注:在香橙派 3.0.6版本的镜像里已经默认自带了python3.10的版本,不需要安装,只需 要后续安装下python3 dev即可。后续统一采用Orangepizero2_3.0.6_ubuntu_jammy_desktop_xfce_linux5.16.17的系统镜像)

Python是一种动态解释型的编程语言。Python可以在Windows、UNIX、MAC等多种操作系统上 使用,也可以在Java、.NET开发平台上使用。

特点

Python使用C语言开发,但是Python不再有C语言中的指针等复杂的数据类型。

Python具有很强的面向对象特性,而且简化了面向对象的实现。它消除了保护类型、抽象类、接口等面 向对象的元素。

Python代码块使用空格或制表符缩进的方式分隔代码。

Python仅有31个保留字,而且没有分号、begin、end等标记。

Python是强类型语言,变量创建后会对应一种数据类型,出现在统一表达式中的不同类型的变量需要做 类型转换。

Python基础知识

简单的基础知识推荐阅读 <www.runoob.com>官网的Python3课程内容

地址如下:

https://www.runoob.com/python3/python3-reg-expressions.html

dict嵌套简单说明

字典(Dictionary)是Python里非常常见的一种数据结构,如果是在其他语言里,一般称做map。是由键(key)和值(value)成对组成,键和值中间以冒 号":“隔开,键值对之间用”,“隔开,整个字典由大括号”{}"括起来。

格式如下:

dict = {key1 : value1, key2 : value2 }

例如:

tinydict = {‘name’: ‘runoob’, ‘likes’: 123, ‘url’: ‘www.runoob.com’}

这里面,键一般是唯一的,如果重复了, 最后的一个键值对(Key:value)会替换前面的。 而且键可以 用数字,字符串或元组充当,用列表不行。而且值就不需要唯一,而且形式多样,比如可以以列表或者 dict的形式出现。 dict的使用非常灵活, 甚至可以和列表组合使用, 列表里能嵌套列表,也能嵌套字典。同样的,字典里 能嵌套字典,字典里也能嵌套列表。 如下面这个例子:

garbage_dict = {'Data': {'Elements': [{'Category': '干垃圾', 'CategoryScore': 
0.8855999999999999, 'Rubbish': '', 'RubbishScore': 0.0}], 'Sensitive': False}, 
'RequestId': '1AB9E813-3781-5CA2-95A0-1EA334E80663'}

这个例子里的dict内容是就是一个嵌套的结构,也就是说,它包含了其他的dict或列表作为值。我们可以 用以下的方式来理解它:

  • 最外层的dict有两个键:‘Data’和’RequestId’。
  • 'Data’对应的值是一个内层的dict,它有两个键:‘Elements’和’Sensitive’。
  • 'Elements’对应的值是一个列表,它包含了一个元素,也就是另一个内层的dict。 • 这个内层的dict有四个键:‘Category’、‘CategoryScore’、‘Rubbish’和’RubbishScore’。
  • ‘Category’对应的值是一个字符串,表示垃圾分类的类别,例如’干垃圾’。
  • 'CategoryScore’对应的值是一个浮点数,表示垃圾分类的置信度,例如0.8856。
  • ‘Rubbish’对应的值是一个字符串,表示垃圾的具体名称,例如’'(空字符串)。
  • RubbishScore’对应的值是一个浮点数,表示垃圾名称的置信度,例如0.0。
  • 'Sensitive’对应的值是一个布尔值,表示是否涉及敏感信息,例如False。
  • ‘RequestId’对应的值是一个字符串,表示请求的唯一标识符,例如’1AB9E813-3781-5CA2-95A0 1EA334E80663’。

C语言调用Python

通过C语言调用Python代码,需要先安装libpython3的 dev依赖库(不同的ubuntu版本下,python版本 可能会有差异, 比如ubuntu 22.04里是libpython3.10-dev)。

首先可以通过以下命令验证是否是否已经存在python3的dev包。

dpkg -l | grep libpython3

正常会有类似如下的输出,出现"libpython3"和 “dev”,如libpython3.10-dev即可:

pg@pg-Default-string:~$ dpkg -l | grep libpython
 ii  libpython3-dev:amd64                       
3.10.6-1~22.04                     
amd64        
header files and a static library for Python (default)
 ii  libpython3-stdlib:amd64                    
3.10.6-1~22.04                     
amd64        
interactive high-level object-oriented language (default 
python3 version)
 ii  libpython3.10:amd64                        
amd64        
Shared Python runtime library (version 3.10)
 ii  libpython3.10-dev:amd64                    
amd64        
amd64        
3.10.12-1~22.04.2                 
3.10.12-1~22.04.2                 
Header files and a static library for Python (v3.10)
 ii  libpython3.10-minimal:amd64                
3.10.12-1~22.04.2                 
Minimal subset of the Python language (version 3.10)
 ii  libpython3.10-stdlib:amd64                 
3.10.12-1~22.04.2                 
amd64        
Interactive high-level object-oriented language (standard 
library, version 3.10)

如果没有, 可以通过apt命令安装相关的dev包:

sudo apt install libpython3.10-dev

直接调用python语句

先看这么一个简单的例子:

#include "Python.h"

int main()
{
    Py_Initialize(); // 初始化
    PyRun_SimpleString("print ('funny')");
    Py_Finalize(); //释放资源
}

然要编译和运行这个程序,可以使用以下命令(假设使用的是gcc编译器和Python 3.10版本):

gcc simpledemo.c -o simpledemo -I /usr/include/python3.10 -lpython3.10 ./simpledemo

输出:

funny

上面代码的解释:

  • 首先包含Python.h头文件,这是Python API的头文件,用于访问Python对象和函数。

  • 其次在程序开始时使用Py_Initialize()函数初始化Python解释器。这样可以在C程序中执行Python代码。

  • 然后使用PyRun_SimpleString()函数执行一段简单的Python代码,例如打印"funny"。需要传递一个字 符串作为参数,表示要执行的Python代码,如print (‘funny’)这么一个Python代码字符串。

函数说明: int PyRun_SimpleString(const char command) 这是针对下面 PyRun_SimpleStringFlags() 的简化版接口,将 PyCompilerFlags 参数设为 NULL;传参就是python执行语句。

最后在程序结束时使用Py_Finalize()函数关闭Python解释器,并释放资源。

调用无参python函数

现在把这条语句放到nopara.py的文件的函数里, 如下:

def say_funny():
	print('funny')

接下来用C语言进行调用,一般调用的流程是这样子的:

#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 *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用 Python函数,并获取返回值。
  7. 使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
  8. 结束时调用void Py_Finalize()函数关闭Python解释器。 相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索): https://docs.python.org/zh-cn/3/c-api/import.html #endif

根据上面的流程写出的示例代码如下:

#include <Python.h>
 int main()
 {
     Py_Initialize();    
     // 初始化
     // 将当前路径添加到sys.path中
     PyObject *sys = PyImport_ImportModule("sys");
     PyObject *path = PyObject_GetAttrString(sys, "path");
     PyList_Append(path, PyUnicode_FromString("."));
     // 导入nopara模块
     PyObject *pModule = PyImport_ImportModule("nopara");
     if (!pModule)
     {
         PyErr_Print();
         printf("ERROR: failed to load nopara.py\n");
         return 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;
     }
     // 调用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/ -lpython3.10 ./nopara

输出:

funny

调用有参python函数

首先定义一个带参数和返回值的函数:

def say_funny(category):
    print(category)
    return category

接下来用C语言进行调用,调用的流程无参函数的调用方式几乎是一样的是的:

#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

无非多了两步,第六步传参

  1. 使用PyObject *Py_BuildValue(const char *format, …)函数创建一个Python元组或者对 象,作为Python函数的参数,没有参数势不需要调用。

这里要注意的是,Py_BuildValue的第一个参数是类型转换:C对应的Python的数据类型转换对应的格式如下:

在这里插入图片描述

和第八步返回值的处理:

  1. 使用int PyArg_Parse(PyObject *args, const char *format, …)函数将返回值转换为C类 型,并检查是否有错误,没有返回值时不需要调用。

示例代码如下:

#include <Python.h>

int main()
{
     Py_Initialize();
     // 将当前路径添加到sys.path中
     PyObject *sys = PyImport_ImportModule("sys");
     PyObject *path = PyObject_GetAttrString(sys, "path");
     PyList_Append(path, PyUnicode_FromString("."));
 // 导入para模块
     PyObject *pModule = PyImport_ImportModule("para");
     if (!pModule)
     {
         PyErr_Print();
         printf("Error: failed to load nopara.py\n");
     }
     
     //获取say_funny函数对象
     PyObject *pFunc = PyObject_GetAttrString(pModule,"say_funny");
     if (!pFunc)
     {
         PyErr_Print();
         printf("Error: failed to load say_funny\n");
     }
     
     //创建一个字符串作为参数
    char *category = "comedy";
    PyObject *pArgs = Py_BuildValue("(s)", category);
    
    
    //调用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 -o para para.c -I /usr/include/python3.10 lpython3.10 ./para

输出:

comedy

//打印返回值
printf(“pValue=%s\n”, result);

//释放所有引用的Python对象
Py_DECREF(pValue);
Py_DECREF(pFunc);
Py_DECREF(pModule);
//释放所有引用的Python对象
Py_Finalize();
return 0;

}


然要编译和运行这个程序,可以使用以下命令(假设使用的是gcc编译器和Python 3.10版本):

```c
gcc -o para para.c -I /usr/include/python3.10 lpython3.10 ./para

输出:

comedy
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值