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

news2024/11/26 0:04:14

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

本人就职于国际知名终端厂商,负责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, Extension

module1 = 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 build

python 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 custom

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

custom.c文件

typedef struct {
	PyObject_HEAD
	PyObject *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.coloradmin.cn/o/969364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JDK8安装及系统变量配置(包含错误处理)

jdk安装 一.下载JDK二.安装三.配置系统变量四.可能遇到的问题1.显示已经安装的问题 或者 读取注册表项值失败2.原因3.解决 五.验证安装成功 一.下载JDK JDK下载官网 二.安装 双击之后&#xff0c;一直下一步就ok 三.配置系统变量 1.找到配置系统变量的地方 2.配置系统变…

[E2E Test] Python Behave Selenium 一文学会自动化测试

前言 本文将使用Python Behave与Selenium&#xff0c;和同学们一起认识自动化测试&#xff0c;并附上完整的实践教程。 项目源码已上传&#xff1a;CSDN 郭麻花 Azure Repo python-behave-selenium 核心概念 1. 什么是E2E Test E2E即End-to-end&#xff0c;意思是从头到尾…

Linux执行命令

命令格式 主命令 选项 参数&#xff08;操作对象&#xff09;例如&#xff1a; 修改主机名 hostname set-hostname 新名称显示/目录下的文件的详细信息 ls -l /命令 内置命令&#xff08;builtin&#xff09;&#xff1a;shell程序自带的命令。 外部命令&#xff1a;有独立…

tensorRT从零起步高性能部署:课程总结

目录 前言1. cuda驱动API2. cuda运行时API3. tensorRT基础4. tensorRT高级5. tensorRT封装6. 自动驾驶案例项目总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&…

1、Flutter移动端App实战教程【环境配置】

一、概述 Flutter是Google用以帮助开发者在IOS和Android 两个平台开发高质量原生UI的移动SDK&#xff0c;一份代码可以同时生成IOS和Android两个高性能、高保真的应用程序。 二、渲染机制 之所以说Flutter能够达到可以媲美甚至超越原生的体验&#xff0c;主要在于其拥有高性…

自然语言处理:提取长文本进行文本主要内容(文本意思)概括 (两种方法,但效果都一般)

本文主要针对长文本进行文本提取和中心思想概括&#xff0c;原文档放在了附件里面&#xff1a;<科大讯飞公告> -----------------------------------方法一&#xff1a;jieba分词提取文本&#xff08;句子赋分法&#xff09;------------------------- 1、首先导入相关…

docker笔记7:Docker微服务实战

1.通过IDEA新建一个普通微服务模块 建Module docker_boot 改POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi…

【聚类】K-Means聚类

cluster&#xff1a;簇 原理&#xff1a; 这边暂时没有时间具体介绍kmeans聚类的原理。简单来说&#xff0c;就是首先初始化k个簇心&#xff1b;然后计算所有点到簇心的欧式距离&#xff0c;对一个点来说&#xff0c;距离最短就属于那个簇&#xff1b;然后更新不同簇的簇心&a…

nginx中模块的设置以及反向代理

nginx设置 nginx http 模块的配置文件位于 "/apps/nginx/conf/nginx.conf"&#xff08;以自己安装时选择的目录为准&#xff0c;若使用yum安装&#xff0c;则在 /etc/nginx/nginx.conf&#xff09;。在该文件中&#xff0c;需要定义一些常见的配置项&#xff0c;包括…

《Communicative Agents for Software Development》全文翻译

《Communicative Agents for Software Development》- 沟通性智能主体促进软件开发 论文信息Abstract1. Introduction2. CHATDEV2.1 聊天链2.2 设计2.3 编码2.4 测试2.5 记录 3. 实验4. 讨论5. 相关工作6. 结论 论文信息 题目&#xff1a;《Communicative Agents for Software…

TienChin 渠道管理-查看渠道接口

自定义 hasPermission 校验规则 自定义一个 Spring Security hasPermission 校验规则&#xff1a; 在 tienchin-framework 模块当中进行自定义&#xff0c;新建 CustomSecurityExpressionRoot.java 自定义 hasPermission 判断逻辑类&#xff1a; /*** author BNTang* version 1…

SSM框架-Spring框架核心知识梳理

目录 一、SpringIoC 1.1认识SpringIoC容器 1.2基于xml配置SpringIoC容器 1.3基于xml配置下Bean的生命周期和作用域 1.4 基于xml与注解配置SpringIoC容器 1.5 基于完全注解类配置SpringIoc容器 二、SpringAop面对切面编程 2.1认识SpringAop面向切面编程 2.2SpringAop基…

list【2】模拟实现(含迭代器实现超详解哦)

模拟实现list 引言&#xff08;实现概述&#xff09;list迭代器实现默认成员函数operator* 与 operator->operator 与 operator--operator 与 operator!迭代器实现概览 list主要接口实现默认成员函数构造函数析构函数赋值重载 迭代器容量元素访问数据修改inserterasepush_ba…

数学建模--二维插值函数模型的Python实现

目录 1.算法实现步骤 2.算法核心代码 3.算法效果展示 1.算法实现步骤 #二维插值函数的展示通过Axes3D函数来进行实现 #我们需要绘制出20*20的插值效果和500*500的插值效果,进行比较. 具体步骤如下所示: 1.将x-y分为20*20并且绘制网格图 2.进行20*20的插值计算并且绘制可视化图…

使用SimPowerSystems并网光伏阵列研究(Simulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

9.4.tensorRT高级(4)封装系列-使用pybind11为python开发扩展模块

目录 前言1. pybind112. 补充知识2.1 pybind11 介绍 总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-使用p…

《网络是怎样连接的》(六)

本文主要取材于 《网络是怎样连接的》 第六章。 目录 6.1 服务器概览 6.2 服务器的接收操作 6.3 Web服务器程序解释请求消息并作出响应 6.4 浏览器接收响应消息并显示内容 简述&#xff1a;本文主要内容是解释 网络包到达服务器之后&#xff0c;如何给客户端响应的。 服务…

电商实战项目(java)知识点整理(持续更新)《苍穹外卖》

一、重要知识点精讲 1.1 nginx反向代理 1. nginx反向代理好处&#xff1a; 1. 提高访问速度&#xff08;可以进行缓存&#xff0c;如果访问相同资源可以直接响应数据&#xff09; 2. 可以进行负载均衡&#xff08;如果没有nginx前端只能固定地访问后端某一台服务器&#xf…

Linux系统编程—socket网络编程

Linux系统编程—socket网络编程 理论概念1. TCP与UDP对比端口号作用 socket开发过程服务端1. socket 创建套接字2. bind 绑定IP端口3. listen 监听客户端4. accept 接收客户端5. read / write 数据传输 客户端1. socket 创建套接字2. connect 连接服务3. read / write 数据传输…

合宙Air724UG LuatOS-Air LVGL API控件--下拉框 (Dropdown)

下拉框 (Dropdown) 在显示选项过多时&#xff0c;可以通过下拉框收起多余选项。只为用户展示列表中的一项。 示例代码 -- 回调函数 event_handler function(obj, event)if (event lvgl.EVENT_VALUE_CHANGED) thenprint("Option:", lvgl.dropdown_get_symbol(obj)…