博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接
本人就职于国际知名终端厂商,负责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, 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;
}