【开发语言】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 调用流程
- 将C语言数据转换为Python格式;
- 用转换后的数据执行对Python接口的函数调用;
- 将调用返回的数据从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 调用流程
- 将C语言数据类型转为Python格式;
- 用转换后的数据执行对Python接口的函数调用;
- 将调用返回的数据从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看到的属性名称
- get、set:对应属性的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;
}
相关文章:

【开发语言】C语言与Python的互操作详解
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...
华为配置聚合vlan(Super vlan--Sub vlan)
聚合vlan,Aggregation vlan,也称Super vlan,可以实现用Sub vlan二层隔离广播域,但又将这些Sub vlan聚合使用同一IP子网和网关的情况。 这样,多个Sub-VLAN共享一个网关地址,节约了子网号、子网定向广播地址、…...

CentOS7安装时直接跳过了安装信息摘要页面的解决方法
最近在配置Hadoop虚拟机的时候,创建的centos7虚拟机在安装信息摘要时直接自动跳过,直接跳到设置用户名和密码,在重复多次的重新删除安装后发现了问题所在: 在进行到选择操作系统来源时,注意是否出现“该操作系统将使用…...

python基础运用例子
python基础运用例子 1、⼀⾏代码交换 a , b :a, b b, a2、⼀⾏代码反转列表 l[::-1]3、合并两个字典 res {**dict1, **dict2}**操作符合并两个字典for循环合并dict(a, **b) 的方式dict(a.items() b.items()) 的方式dict.update(other_dict) 的方式 4、⼀⾏代码列…...

k8s基本概念
一、什么是Kubernetes二:Kubernetes部署方式的演变三、为什么要用K8S四、K8S的特性五、Kubernetes 集群架构与组件5.1 Master 组件① Kube-apiserver② Kube-controller-manager③ Kube-scheduler④ AUTH 认证模块 5.2 配置存储中心5.3 Node 组件① Kubelet② Kube-…...
Python exp() 函数
描述 exp() 方法返回x的指数,ex。 语法 以下是 exp() 方法的语法: import mathmath.exp( x ) 注意:exp()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。 参数 x -- 数值表达式。 返回值 返回x的指数,ex。 实例 以下展…...
Day 34 贪心算法 part03 : 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果
134. 加油站 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas…...

气象站的构成及功能应用
气象站是一种用于观测、记录和报告天气数据的设备。它是由数据采集系统、通讯系统、供电系统和立杆支架构成。 一、气象站的构成: 数据采集系统:用于测量气温、湿度、风速、风向、气压、降雨量、雪深等气象参数。 通讯系统:收集和处理传感…...

Qt中布局管理使用总结
目录 1. 五大布局 1.1 QVBoxLayout垂直布局 1.2 QHBoxLayout水平布局 1.3 QGridLayout网格布局 1.4 QFormLayout表单布局 1.5 QStackedLayout分组布局 1.6 五大布局综合应用 2. 分割窗口 3. 滚动区域 4. 停靠区域 1. 五大布局 1.1 QVBoxLayout垂直布局 #include <…...

(位运算) 剑指 Offer 15. 二进制中1的个数 ——【Leetcode每日一题】
❓ 剑指 Offer 15. 二进制中1的个数 难度:简单 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。 提示ÿ…...

基于SSM的新能源汽车在线租赁系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用Vue技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...
CTF 代码审计之绕过过滤的空白字符
题目 <?php header("Content-Type:text/html;charsetutf-8"); highlight_file(02kbzf.php);//引入名为 flag2.php 的文件。 include(f . lag2 . .php);//初始化变量 $info 和 $req。 $info ""; $req [];//读取文件 flag2.php 的内容到变…...

【Vue】 Vue3 安装说明,适合小白新手
1、独立版本 我们可以在 Vue.js 的官网上直接下载最新版本, 并用 下载 Vue.js https://unpkg.com/vuenext 2、使用 CDN 方法 以下推荐国外比较稳定的两个 CDN,国内还没发现哪一家比较好,目前还是建议下载到本地。 Staticfile CDN(国内&am…...

电脑提示“系统找不到指定的文件”怎么办?
“系统找不到指定的文件”对于Windows用户来说是一个很常见的错误,尤其是Win10用户,经常会遇到Win10提示找不到指定文件。在此错误后面有时还会出现错误代码:0x80070002,但是,故障类型或代码在不同的操作系统规范上是不…...

向openssl中添加一个最简单的算法
文章目录 一、尝试在sha.c中添加新的函数二、添加自定义算法2.1 添加对应文件2.2 相关配置2.3 编译运行 一、尝试在sha.c中添加新的函数 在尝试添加新算法前,我先尝试在原有的旧算法中添加一个新函数,看是否能被编译并生成对应的动态链接库。 关于open…...
自己公司开发的ERP系统,怎么对接京东,淘宝等这些电商平台?
得益于互联网基建的成熟及快速发展的电子商贸经济,我国线上零售市场快速增长,2022年全国线上零售额达到13.79万亿元,占社会消费品零售总额的比重为27.2%,也就是说每卖出三件零售商品,就有一件是从线上销售。中大型零售…...

联想集团财报不及华尔街预期,财务业绩恐将继续恶化
来源:猛兽财经 作者:猛兽财经 华尔街对联想集团财报的预测 在联想集团(00992)公布2024财年第一季度财务业绩之前,华尔街分析师就曾预测,联想集团的收入和利润将实现强劲增长。 具体而言,根据S&…...
计网基础面试题
浏览器输入网址之后发生什么 1,DNS解析过程 2,三次握手 3,TLS通信 4,发送数据 5,四次挥手 TCP三次握手和四次挥手 两台计算机通信的过程 局域网通信———交换机——MAC地址 广域网通信———路由器——IP地址 网…...

设置Linux CentOS7桥接模式连网
在虚拟机上安装centos7系统后,首要任务就是设置网络。 我们在文章《设置linux centos7连接网络》中讨论了如何设置NAT模式连网。本文讨论如何在设置好NAT模式后,调换为桥接模式。 仍采用图形化方式设置方法。 一、查看物理机网络 把虚拟机设置为桥接…...

Mysql底层数据结构为什么选择B+树
索引底层采用什么数据结构,为什么使用B树而不是其他数据结构: (1)如果采用二叉树:使用递增字段作为索引时,二叉树会退化成链表,查找效率太低 (2)如果采用红黑树…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...