【开发语言】C语言与Python的互操作详解

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G/6G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解

文章目录

  • C语言与Python的互操作详解
  • 一、C语言调用Python实现方法
    • 1.1 调用流程
    • 1.2 关键结构体和函数介绍
    • 1.3 执行简单的Python语句
    • 1.4 执行文件中的Python语句
    • 1.5 Python模块加载和库函数函数调用
    • 1.6 C语言数据类型与Python数据类型的转换
    • 1.7 创建Python数据对象以及使用builtins函数
  • 二、Python调用C语言实现方法
    • 2.1 调用流程
    • 2.2 将C函数打包成module
    • 2.3 如何定义一个类
    • 2.4 定义module的关键函数
    • 2.5 构建setup脚本,将C语言编译成so、pyd等格式
  • 三、C语言与Python的互操作示例
    • 3.1 C语言调用Python
      • demo.py文件
      • main.c文件
    • 3.2 Python调用C语言
      • main.py文件
      • custom.c文件



C语言与Python的互操作详解


在这里插入图片描述

官方文档介绍:https://docs.python.org/zh-cn/3/extending/index.html

由于Python可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,必须先包含#include<Python.h>。并且推荐总是在Python.h前定义#define PY_SSIZE_T_CLEAN

一、C语言调用Python实现方法

1.1 调用流程

  1. 将C语言数据转换为Python格式;
  2. 用转换后的数据执行对Python接口的函数调用;
  3. 将调用返回的数据从Python转换为C格式;

1.2 关键结构体和函数介绍

使用下面的函数初始Python环境:

PyConfig_InitPythonConfig()  # 初始化一个PyConfig对象
PyConfig_Read()              # 读取当前环境的配置信息
Py_InitializeFromConfig()    # 使能客制化的Python环境

其中一个重要的结构图是PyConfig,几个关键的属性含义如下:

  • module_search_paths_set # 只有设置为1时,下面的变量才生效
  • module_search_paths # 增加指定的搜索路径

1.3 执行简单的Python语句

使用下面的函数可以执行简单的Python语句:

# 执行字符串参数中的Python语句
PyRun_SimpleString()#例如:
PyRun_SimpleString("import sys")

1.4 执行文件中的Python语句

使用下面的函数可以执行文件中的Python语句:

# 执行字符串参数中的Python语句
PyRun_SimpleFile()# 例如:
FILE *fp = fopen("path/to/main.py", "r")
PyRun_SimpleFile(fp, "path/to/main.py")

1.5 Python模块加载和库函数函数调用

下面介绍如何加载Python模块,并调用模块中的函数:

PyImport_ImportModule()    # 加载指定的Python模块
PyObject_GetAttrString()   # 获取模块中的函数或者成员
PyCallable_Check()         # 检测获取的模块对象是否可以调用
PyTuple_New()              # 当从C调用Python函数的时候,入参必须使用元组封装,此函数创建一个元组对象
PyObject_CallObject()      # 调用Python函数

1.6 C语言数据类型与Python数据类型的转换

参考官网API:https://docs.python.org/zh-cn/3/c-api/stable.html

总结的命名规则如下:

  • 将Python数据类型转换为C语言数据类型
    Py<Python类型>_As<C语言数据类型>
  • 将C语言数据类型转换为Python数据类型
    Py<Python类型>_From<C语言数据类型>

1.7 创建Python数据对象以及使用builtins函数

  • 如果要使用Python中的数据类型,可以在官网查找
    Py<Python类型>_XXX
  • 如果要使用Python builtins函数,可以在查找
    Py<Python基础库>_XXX

二、Python调用C语言实现方法

2.1 调用流程

  1. 将C语言数据类型转为Python格式;
  2. 用转换后的数据执行对Python接口的函数调用;
  3. 将调用返回的数据从Python转换为C语言格式;

2.2 将C函数打包成module

我们需要将C变量和方法封装成类(也可以定义module级别的方法),然后打包成一个module发布出来,之后Python便可以使用C函数了。下面介绍两个关键的数据结构。

  • PyModuleDef
    在这里插入图片描述

    • m_base :是基类,应该总是PyModuleDef_HEAD_INIT
    • m_name:模块的名字
    • m_size :目前就设置为-1,一些高级用法会用到这个参数
    • m_methods:模块方法列表
  • PyMethodDef
    在这里插入图片描述

    • ml_name:Python看到的方法名称
    • ml_meth :对应的C函数名
    • ml_flags :指明函数是否有参数

2.3 如何定义一个类

定义一个类的关键数据类型是PyTypeObject,这个类型中定义了一些类的属性:

  • tp_name:类的名字(格式为modulename.classname)
  • tp_basicsize:类的大小,用于分配空间
  • tp_itemsize:如果是静态类则为0,如果是动态类则非0
  • tp_flags:类的属性参数,至少应该为Py_TPFLAGS_DEFAULT
  • tp_new:类的实例化函数
  • tp_init:类的初始化器
  • tp_dealloc:类的析构函数
  • tp_members:成员列表
  • tp_methods:方法列表(结构同module)
  • tp_getset:属性get和set函数

涉及成员定义的结构体PyMemberDef,关键成员含义:

  • name :Python中看到的成员名称
  • type:成员类型
  • offset:成员在结构体中的偏移量,使用offset()函数获取

定义属性的get和set方法的结构体PyGetSetDef,其关键成员含义:

  • name :Python看到的属性名称
  • getset:对应属性的get、set方法

2.4 定义module的关键函数

当在Python中调用我们定义的模块时,会调用一个PyMODINIT_FUNC PyInit_<moduleName>(void)函数。一个简单的PyInit_(void)实现流程为:

  • 使用PyType_Ready()为我们定义的静态类分类内存空间;
  • 使用PyModule_Create()创建module;
  • 然后使用PyModule_AddObject()将我们定义的类注册到module中;

详细过程可以看下面的demo

2.5 构建setup脚本,将C语言编译成so、pyd等格式

# file name 'setup.py'from distutils.core import setup, Extensionmodule1 = Extension('moduleName', sources = ['moduleName.c'])setup (name = 'moduleName'version = '1.0'description = 'This is a Demo'ext_modules = [module1 ])

将上面代码中的moduleName替换为你的module名称,在sources中添加对应的C文件,不需要添加头文件。使用下面的命令编译和安装:

python setup.py buildpython setup.py install

当然现在有很多库实现了python调用C语言,例如

  • Cython
  • cffi
  • ctypes
  • SWIG

三、C语言与Python的互操作示例

3.1 C语言调用Python

demo.py文件

def print_c_point(p)print(p)

main.c文件

#define PY_SSIZE_T_CLEAN
#include <Python.h>PyStatus init_python(const char *program_name, const wchar_t *additional_search_path)
{assert(program_name);PyStatus status;PyConfig config;PyConfig_InitPythonConfig(&config);status = PyConfig_SetBytesString(&config, &config.program_name, program_name);if(PyStatus_Exception(status)){goto done;}status = PyConfig_Read(&config)if(PyStatus_Exception(status)){goto done;}if(additional_search_path){config.module_search_paths_set = 1;status = PyWideStringList_Append(&config.module_search_paths, additional_search_path);if(PyStatus_Exception(status)){goto done;}}status = Py_InitializeFromConfig(&config);done:PyConfig_Clear(&config);return status;
}int main(int argc, char *argv[])
{init_python(argv[0], NULL);PyRun_SimpleString("from time import time, ctime\n""print('Today is', ctime(time()))\n");File *fp = fopen("path/to/demo.py", "r");PyRun_SimpleFile(fp, "path/to/demo.py");PyObject *pyModule, *pyFunc;PyObject *pyArgs, *pyValue;pyModule = PyImport_ImportModule(demo.py);if(!pyModule){PyErr_Print();goto end;}pyFunc = PyObject_GetAttrString(pyModule, print_c_point);if(!pyFunc){Py_DECREF(pyModule);PyErr_Print();goto end;}if(PyCallable_Check(pyFunc)){pyArgs = PyTuple_New(1);for(int i=0;i < 1;++i){pyValue = PyLong_FromLong(3);if(!pyValue){Py_DECREF(pyArgs);PyErr_Print();goto end;}PyTuple_SetItem(pyArgs, i, pyValue);}pyValue = PyObject_CallObject(pyFunc, pyArgs);Py_DECREF(pyArgs);if(pyValue){printf("The result is %ld.\n". PyLong_AsLong(pyValue));Py_DECREF(pyValue);} else {PyErr_Print();goto end;}}Py_DECREF(pyFunc);Py_DECREF(pyModule);end:if(Py_FinalizeEx() < 0)exit(-1);return 0;
}

3.2 Python调用C语言

main.py文件

import customif '__main__' == __name__:use_custom("custom module", 1234)

custom.c文件

typedef struct {PyObject_HEADPyObject *user_name;unsigned int passwd;
} customObject;static int 
custom_clear(customObject *self)
{Py_CLEAR(self->user_name);return 0;
}static void
custom_dealloc(customObject *self)
{PyObecjt_GC_UnTrack(self);custom_clear(self);Py_TYPE(self)->tp_free((PyObject*) self);
}static PyObject*
custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{customObject *self;self = (customObject *)type->tp_alloc(type, 0);if(self != NULL){self->user_name = PyUnicode_FromString("");if(self->user_name == NULL){Py_DECREF(self);return NULL;}self->passwd = 1234;}return (PyObject *) self;
}static int
custom_init(customObject *self, PyObject *args, PyObject *kwds)
{static char *kwlist[] = {"user_name","passwd",NULL};PyObject *user_name = NULL, *tmp;if(!PyArg_ParseTupleAndKeywords(args, kwds, "|UkI", kwlist, &user_name, &self->passwd))return -1;if(user_name){tmp = self->user_name;Py_INCREF(user_name);self->user_name = user_name;Py_DECREF(tmp);}return 0;
}static PyMemberDef 
custom_members[] = {{"passwd", T_ULONG, offset(customObject, passwd), 0, "user password"},{NULL}
};static PyObject *
custom_getusername(customObject *self, void *closure)
{Py_INCREF(self->user_name);return self->user_name;
}static PyObject *
custom_setusername(customObject *self, PyObject *value, void *closure)
{if(value == NULL) {PyErr_SetString(PyExc_TypeError, "user name is not NULL");return -1;}if(!PyUnicode_Check(value)) {PyErr_SetString(PyExc_TypeError, "user name should string");return -1;}Py_INCREF(value);Py_CLEAR(self->user_name);self->user_name = value;return 0;
}static int
custom_getpassword(customObject *self, void *closure)
{PyObject *tmp = PyLong_FromUnsignedLong(self->passwd);Py_INCREF(tmp);return tmp;
}static int
custom_setpassword(customObject *self, PyObject *value, void *closure)
{if(value == NULL) {PyErr_SetString(PyExc_TypeError, "user password is not NULL");return -1;}if(!PyLong_Check(value)) {PyErr_SetString(PyExc_TypeError, "user password should integer");return -1;}self->passwd = PyLong_AsUnsignedLong(value);return 0;
}static PyGetSetDef
custom_getsetters[] = {{"user_name", (getter)custom_getusername, (setter)custom_setusername, "user name", NULL},{"passwd", (getter)custom_getpassword, (setter)custom_setpassword, "user password", NULL},{NULL}
};static PyObject*
custom_printUserInfo(customObject *self, PyObject *Py_UNUSED(ignored))
{printf("user name is %s and password is %ld.\n",self->user_name,self->passwd);
}static PyMethodDef custom_methods[] = {{"custom_printUserInfo", (PyCFunction) custom_printUserInfo, METH_NOARGS, "print user info"},{NULL}
};static PyTypeObject customType = {PyVarObject_HEAD_INIT(NULL,0).tp_name = "custom.custom",.tp_doc = PyDoc_STR("custom object"),.tp_basicsize = sizeof(customObject),.tp_itemsize = 0,.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,.tp_new = custom_new,.tp_init = (initproc)custom_init,.tp_dealloc = (destructor) custom_dealloc,.tp_clear = (inquiry) custom_clear,.tp_members = custom_members,.tp_methods = custom_methods,.tp_getset = custom_getsetters,
};static PyModuleDef custommodule = {PyModuleDef_HEAD_INIT,.m_name = "custom",.m_doc = "example module that creates an extension type",.m_size = 1
};PyMODINIT_FUNC
PyInit_custom(void)
{PyObject *m;if(PyType_Ready(&customType) < 0)return NULL;m = PyModule_Create(&custommodule);if(m == NULL) return NULL;Py_INCREF(&customType);if(PyModule_AddObject(m, "custom", (PyObject*)&customType) < 0){Py_DECREF(&customType);Py_DECREF(m);return NULL;}return m;
}


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/121080.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鼠标悬停阴影的效果被旁边div挡住的解决办法

出现的问题 需求要求鼠标悬停某个图片上有阴影效果&#xff0c;但阴影被旁边相邻的div挡住了&#xff0c;如图所示 解决方案 给悬停的这块div增加2个css属性 $(this).css(position, relative); $(this).css(z-index, 200);新的效果如图所示 一直写后端&#xff0c;前端的…

【100天精通Python】Day50:Python Web编程_Django框架从安装到使用

目录 1 安装Django Web框架 2 创建一个Django 项目 3 数据模型 3.1 在应用程序的 models.py 文件中定义数据模 3.2 创建模型的迁移文件并应用 3.2.1 查询模型对象&#xff1a; 3.2.2 创建新模型对象&#xff1a; 3.2.3 更新模型对象&#xff1a; 3.2.4 删除模型对象&a…

【C++ 学习 ⑲】- 多态(下)

目录 一、虚函数表和多态的原理 1.1 - 虚函数表 1.2 - 多态的原理 二、单继承和多继承关系中的虚函数表 2.1 - 单继承关系中的虚函数表 2.2 - 多继承关系中的虚函数表 三、纯虚函数和抽象类 一、虚函数表和多态的原理 1.1 - 虚函数表 问&#xff1a;sizeof(b) 是多少&a…

【9月比赛合集】9场可报名的「创新应用」、「数据分析」和「程序设计」大奖赛,任君挑选!

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 创新应用赛&#xff08;2场比赛&#xff09;程序设计赛&#…

会话跟踪技术学习笔记(Cookie+Session)+ HTTP学习笔记

一、核心知识点&#xff08;重点&#xff09;&#xff1a; 1.1 Cookie 1. Cookie&#xff1a;是一种客户端会话技术&#xff0c;数据会被保存在客户端&#xff0c;Cookie会携带数据访问服务器&#xff0c;用以完成一次会话内多次请求间的数据共享 2. 过程&#xff1a;浏览器…

Android 1.2 开发环境搭建

目录 1.2 开发环境搭建 1.JDK安装与配置 2.开发工具二选一 3.相关术语的解析 4.ADB命令行的一些指令 5.APP程序打包与安装的流程&#xff1a; 6.APP的安装过程&#xff1a; 7.本节小结 1.2 开发环境搭建 现在主流的Android开发环境有: ①Eclipse ADT SDK ②Android Stu…

数据库概念

定义&#xff1a; 数据库&#xff08;Database 简称DB&#xff09;是持久存储有组织/可共享数据/的容器 数据库管理系统(MySQL、Oracle、DB2)是操作/和管理数据库/的软件 分类&#xff1a; 关系(型)数据库 (MySQL、Oracle、SQL Server、SQLite、DB2) 非关系(型)数据库 (Redis…

长胜证券:华为“黑科技”点燃A股炒作激情

8月29日&#xff0c;在未举行相关发布会的情况下&#xff0c;华为新款手机Mate60Pro悄然上线开售&#xff0c;并在一小时内售罄。 金融出资报记者注意到&#xff0c;跟着商场对新机重视的继续发酵&#xff0c;其中的各种技能打破也愈加受到重视&#xff0c;其影响很快扩散到资…

C++ 学习之 构造函数 和 析构函数

前言 总的来说&#xff0c;构造函数负责对象的初始化&#xff0c;而析构函数负责对象的清理和资源释放。它们是C面向对象编程中非常重要的概念&#xff0c;用于管理对象的生命周期&#xff0c;确保对象在创建和销毁时都能够正确地进行初始化和清理。 正文 看代码 class perso…

python实现pdf双页文档转png图片,png图片裁剪为左右两等分,再合并为新的pdf单页文档

一、问题引入 现有pdf双页文档如下&#xff1a; 现按照以下页码次序对pdf双页文档进行裁剪和拼接&#xff0c;其中有两点需要特别注意&#xff0c;一是封面页只裁剪中间部分&#xff0c;二是文档是从右往左的顺序排版的 二、python程序 import os import office from PIL …

STM32G030F6 (SOP-20)Cortex ® -M0+, 32KB Flash, 8KB RAM, 17 GPIOs

淘宝淘了一批 STM32G030F6P6 SOP20&#xff0e;先备注一下, 还没想到能干嘛用&#xff0e; 手上的 STM32F103C6T6还剩一些&#xff0e; 一堆 “淘宝原厂STM32F103C8T6”, 还烫着手. 理解信息: ( 逐步补充 ) System Clock GPIOs GPIOs 17 PA[7:0] : 8bits USART Timer ADC I2…

Configuring HSRP(Hot Standby Routing Protocol)

文章目录 热备份路由协议&#xff08;HSRP&#xff09;HSRP(Hot Standby Routing Protocol)工作原理在思科路由器上配置HSRP1. 路由器配置2. 分配IP地址3. 配置HSRP4. 关键指令注释5. 验证配置是否生效6.HSRP抢占配置7.HSRP Group安全配置8.HSRP切换时间更改9.HSRP版本配置10.H…

App自动化测试持续集成效率提高50%

持续集成是一种开发实践&#xff0c;它倡导团队成员需要频繁的集成他们的工作&#xff0c;每次集成都通过自动化构建&#xff08;包括编译、构建、自动化测试&#xff09;来验证&#xff0c;从而尽快地发现集成中的错误。让正在开发的软件始终处于可工作状态&#xff0c;让产品…

SpringBoot 博客网站

SpringBoot 博客网站 系统功能 登录注册 博客列表展示 搜索 分类 个人中心 文章分类管理 我的文章管理 发布文章 开发环境和技术 开发语言&#xff1a;Java 使用框架: SpringBoot jpa H2 Spring Boot是一个用于构建Java应用程序的开源框架&#xff0c;它是Spring框架的一…

【sgTransfer】自定义组件:带有翻页、页码、分页器的穿梭框组件,支持大批量数据的穿梭显示。

特性&#xff1a; 表格宽度可以自定义翻页器显示控件可以自定义列配置项可以设置显示字段列名称、宽度、字段名可以配置搜索框提示文本&#xff0c;支持搜索过滤穿梭框顶部标题可以自定义左右箭头按钮文本可以设置 sgTransfer源码 <template><div :class"$opti…

【树形权限】树形列表权限互斥选择、el-tree设置禁用等等

文章目录 一、实现如上树形列表1.1 首先要就是渲染树形列表1.2 然后通过插槽处理头部标题1.3 再通过插槽处理表格body体内容1.4 让body体中的选框和表头中的选框产生关联 二、将 el-tree 整棵树设为禁用状态三、动态表格合并 需求&#xff1a;按照权限管理配置的数据权限树展开…

Vert.x 源码解析(4.x)(一)——Future源码解析

目录 1. 简介 在现代的软件开发中&#xff0c;异步编程已经变得非常重要。它可以提高应用程序的并发性能&#xff0c;使应用程序能够更有效地处理大量的并行操作。Vert.x 是一个面向事件驱动、非阻塞的异步编程框架&#xff0c;它提供了丰富的工具来简化异步编程的复杂性。 如…

解决windows下git操作提示用户名密码错误的问题

当代码从一个平台切换到另一个平台的时候&#xff0c;需要做两步操作&#xff0c;第一步就是更新git的仓库地址&#xff0c;在项目的.git/config文件里面修改&#xff0c;这一步做完之后&#xff0c;就可以推送代码到新的仓库了&#xff0c;这里就是重点来了。 一般第一次推动代…

JVM虚拟机对象探秘

对象的创建 Java是一门面向对象的编程语言&#xff0c;创建对象通常只是通过new关键字。 对象创建过程 当Java虚拟机遇到一条字节码new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到 一个类的符号引用&#xff0c;并且检查这个符号引用&#xff08;类…

如何设计微服务

一、序幕 最近在思考&#xff0c;自己哪些不足&#xff0c;需要学习点什么&#xff1f;看着Java基础知识&#xff0c;千遍一律&#xff0c;没有太大的动力需深挖&#xff0c;只能在写业务项目的时候边写边思考边夯实自己的基础。于是看了网上的一些资料&#xff0c;结合以前面试…