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


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

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


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



    C语言与Python的互操作详解


    在这里插入图片描述

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

    由于Python可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,必须先包含#include。并且推荐总是在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环境
    
    • 1
    • 2
    • 3

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

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

    1.3 执行简单的Python语句

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

    # 执行字符串参数中的Python语句
    PyRun_SimpleString()
    
    #例如:
    PyRun_SimpleString("import sys")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.4 执行文件中的Python语句

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

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

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

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

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

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

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

    总结的命名规则如下:

    • 将Python数据类型转换为C语言数据类型
      Py_As
    • 将C语言数据类型转换为Python数据类型
      Py_From

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

    • 如果要使用Python中的数据类型,可以在官网查找
      Py_XXX
    • 如果要使用Python builtins函数,可以在查找
      Py_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_(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 ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    python setup.py build
    
    python setup.py install
    
    • 1
    • 2
    • 3

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

    • Cython
    • cffi
    • ctypes
    • SWIG

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

    3.1 C语言调用Python

    demo.py文件

    def print_c_point(p)
    	print(p)
    
    • 1
    • 2

    main.c文件

    #define PY_SSIZE_T_CLEAN
    #include 
    
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    3.2 Python调用C语言

    main.py文件

    import custom
    
    if '__main__' == __name__:
    	use_custom("custom module", 1234)
    
    • 1
    • 2
    • 3
    • 4

    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;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183


    在这里插入图片描述

  • 相关阅读:
    Oracle 数据库 varchar2 从 4000 扩展到 32k
    类与对象(二)
    计算机网络:PPP协议与HDLC协议
    Flutter系列文章-Flutter基础
    Linux之FinalShell的安装和使用
    OPPO A2 Pro正式发布,媒介盒子多家媒体报道
    体系结构28_多处理机(2)
    快速分割视频,并提取原视频中的音频单独保存
    11-09 周四 机器学习开发容器搭建
    docker运行并进入MySQL容器
  • 原文地址:https://blog.csdn.net/qq_31985307/article/details/132642686