C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

时间:2024-05-18 16:52:44

一:环境配置

注:我使用的环境:IDLE (Python 3.6 32-bit)  ;vs2010

以下环境配置部分详见:混合编程之——C++调用python2.7&python3.5

以python3.6为例:

需要单独将Python36-32下面的,include文件夹里面的头文件和libs文件夹里面的库函数都单独拷贝出来,至于怎么设置,怎么放,咱们慢慢来。
1、新建一个文件夹,取名为test_python36;
2、将Python36-32下面的,include文件夹、libs文件夹都拷贝到test_python36文件夹中,并把libs文件夹中的python36.lib拷贝一份并命名为python36_d.lib放在该文件下(libs下);C调用Python(传递数字、字符串、list数组(一维、二维),结构体)
3、打开VS2010,新建一个项目(C_python),选好路径,文件夹test_python36里面的内容如下:

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

4、接下来需要配置项目(C_python)的环境变量

右键项目-》属性

(1)添加头文件

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

(2)添加库文件 

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)


5、生成解决方案,不会报错,但肯定没有结果,因为还没有添加动态链接库文件调用的python原函数。具体做法:在生成的Debug路径下添加调用的python原函数(例如:Test001.py),如下图所示

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

6、在运行程序过程中遇到的报错和解决办法:

(1)Cannot find or open the PDB file问题的解决

详见:http://blog.chinaunix.net/uid-11765716-id-3074932.html

(2)无法解析的外部符号 __imp__py_reftotal(或其他)

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

详见:https://www.jb51.net/article/108588.htm

 

 

二:进行C++/C调用Python的代码实现

1、c++ 调用python的过程:

   1)初始化Python调用环境

    2)加载对应的Python模块

    3)加载对应的Python函数

   4)将参数转化为Python元组类型

   5)调用Python函数并传入参数元组

   6)获取返回值

   7)根据Python函数的定义解析返回值

注:在C中使用到的部分参数类型参照:详见:https://docs.python.org/3.6/c-api/arg.html

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

2、传递并返回数字

C部分:

//调用Add函数,传两个int型参数
void Add(){
	int x=6,y=8;
	Py_Initialize();

	PyObject * pModule = NULL;
	PyObject * pFunc = NULL;
	PyObject *pReturn = NULL;
    //加载python模块
	pModule = PyImport_ImportModule("Test001");//Test001:Python文件名
    //加载对应的Python函数
	pFunc = PyObject_GetAttrString(pModule, "Add");//Add:Python文件中的函数名

	//创建参数:
	PyObject *pArgs = PyTuple_New(2);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数
	PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", x));//0--序号,i表示创建int型变量
	PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", y));//1--序号

	//返回值
	pReturn = PyEval_CallObject(pFunc, pArgs);//调用函数

	//将返回值转换为int类型
	int result;
	PyArg_Parse(pReturn, "i", &result);//i表示转换成int型变量
	printf("返回的结果result:%d + %d + 3= %d\n",x,y,result);
	//cout << "6 + 8 = " << result << endl;

	Py_Finalize();
}

Python部分:

def Add(a, b):
    print(a,' + ',b,'=',a+b)
    return a+b+3

运行结果:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

3、传递并返回字符串

C部分:

void Hello(){

	Py_Initialize();//调用Py_Initialize()进行初始化
	PyObject * pModule = NULL;
	PyObject * pFunc = NULL;
	PyObject *pReturn = NULL;

	pModule = PyImport_ImportModule("Test001");//调用的Python文件名
	pFunc = PyObject_GetAttrString(pModule, "Hello");//调用的函数名
	//创建参数:
	PyObject *pArgs = PyTuple_New(1);
	PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", "Hello Python!!"));
	
	pReturn=PyEval_CallObject(pFunc, pArgs);//调用函数,并传入参数pArgs
	char * result;
	PyArg_Parse(pReturn, "s", &result);
	printf("返回值:%s\n",result);
	Py_Finalize();//调用Py_Finalize,和Py_Initialize相对应的.

}

Python部分:

def Hello(s):
    print(s)
    return "Hello C!!"

运行结果:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

4、传递并返回一维list数组

C部分:

void szTest(){
	/*Pass by List: Transform an C Array to Python List*/
    double CArray[] = {1.2, 4.5, 6.7, 8.9, 1.5, 0.5};

	Py_Initialize();
	PyObject * pModule = NULL;
	PyObject * pFunc = NULL;
	PyObject *pDict = NULL;
	PyObject *pReturn = NULL;

	pModule = PyImport_ImportModule("Test001");
	pDict = PyModule_GetDict(pModule); 
    pFunc = PyDict_GetItemString(pDict, "szTest");
    cout<<"C Array Pass Into The Python List:"<<endl;
	
    PyObject *PyList  = PyList_New(6);//定义一个与数组等长的PyList对象数组
    PyObject *ArgList = PyTuple_New(1);//定义一个Tuple对象,Tuple对象的长度与Python函数参数个数一直,上面Python参数个数为1,所以这里给的长度为1
    for(int i = 0; i < PyList_Size(PyList); i++)
        PyList_SetItem(PyList,i, PyFloat_FromDouble(CArray[i]));//给PyList对象的每个元素赋值
    PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中
    pReturn=PyObject_CallObject(pFunc, ArgList);//调用函数,完成传递
	
	if(PyList_Check(pReturn)){ //检查是否为List对象
		printf("返回的结果result: ");
		int SizeOfList = PyList_Size(pReturn);//List对象的大小,这里SizeOfList = 
		for(int i = 0; i < SizeOfList; i++){
			PyObject *Item = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素
            int result;
			PyArg_Parse(Item, "i", &result);//i表示转换成int型变量
			printf("%d ",result);
			//cout << PyInt_AsLong(Item) <<" "; //输出元素
			Py_DECREF(Item); //释放空间
         }
		printf("\n");
	}else{
		cout<<"Not a List"<<endl;
	}

	Py_Finalize();
}

Python部分:

def szTest(List):
    print (List)
    
    IntegerList = [1, 2, 3]
    return IntegerList
    

运行结果:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

5、传递并返回二维list数组

list object(介绍C调用Python中,关于list的一些函数)

C部分:

void szSecTest(){
	long CArray[2][3]={{2,3,4},{5,6,7}};

	Py_Initialize();
	PyObject * pModule = NULL;
	PyObject * pFunc = NULL;
	PyObject *pDict = NULL;
	PyObject *pReturn = NULL;

	pModule = PyImport_ImportModule("Test001");
	pDict = PyModule_GetDict(pModule); 
    pFunc = PyDict_GetItemString(pDict, "szSecTest");
	 cout<<"C two-dim Array Pass Into The Python List:"<<endl;
	
    PyObject *PyList  = PyList_New(0);//定义该PyList对象为0和PyList_Append有关,相当于定义PyList为[]
    PyObject *ArgList = PyTuple_New(1);
    for(int i = 0; i <2; i++){
		PyObject *PyList1  = PyList_New(3);
		for(int j = 0; j < 3; j++){
			
			PyList_SetItem(PyList1,j, PyLong_FromLong(CArray[i][j]));
		}
		PyList_Append(PyList,PyList1);//PyList_Append可以参考Python中,list的append的用处
	}
	PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中
    pReturn=PyObject_CallObject(pFunc, ArgList);//调用函数,返回一个list

	if(PyList_Check(pReturn)){ //检查是否为List对象
		int SizeOfList = PyList_Size(pReturn);//List对象的大小,这里SizeOfList = 2
		printf("返回的结果result:\n");
		for(int i = 0; i < SizeOfList; i++){
			PyObject *ListItem = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素
			int NumOfItems = PyList_Size(ListItem);//List对象子元素的大小,这里NumOfItems = 3
			for(int j = 0; j < NumOfItems; j++){
				PyObject *Item = PyList_GetItem(ListItem, j);//遍历List对象中子元素中的每个元素
				int result;
				PyArg_Parse(Item, "i", &result);//i表示转换成int型变量
				printf("%d ",result);
				//cout << PyInt_AsLong(Item) <<" "; //输出元素
				Py_DECREF(Item); //释放空间
			}
			 cout<<endl;
			Py_DECREF(ListItem); //释放空间
		}
	}else{cout<<"Not a List"<<endl;}

}

Python部分:

def szSecTest(List):
    print (List)
    
    IntegerList = [[1, 2, 3],[2,3,4]]
    return IntegerList

运行效果:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

6、传递并返回结构体

1)Python:struct模块的pack、unpack

  • 有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.

简单举一个例子如下:

print('===== pack - unpack =====' )
buf1 = 2
buf2 = 3
buf4 = 4
retResult = struct.pack('ii4si', buf1, buf2, b'abd', buf4)
print(type(retResult))   #<class 'bytes'>
a1,a2,a3,a4=struct.unpack("ii4si",retResult)
print(a1,a2,bytes.decode(a3),a4)

结果图:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

  • struct.pack(fmt,v1,v2,v3...)     按fmt这个格式把后面数据给封装成指定的数据;返回一个包含了v1,v2等的字节对象,数据是通过fmt格式化后的。参数必须和fmt需要格式化的完全对应起来
  • struct.unpack(fmt,buff)            按fmt这个格式把字节流转成原组tuple;pack的逆向操作,只有一个对象也返回tuple。
  • 下面是fmt这个格式在python3.6的说明:详细请见:struct 的官方文档(Format)

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

 

2)Python中str与bytes的互相转换

a = "asdf"
b = a.encode('utf-8')#str转化为bytes
c = bytes.decode(b)#bytes转化为str
print(a,type(a),b,type(b),c,type(c))

结果图:C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

 

3)以下部分为C调用Python,传递结构体的代码部分

C部分:

typedef struct header_ {
	int buf1;
	int buf2;
	char buf3[12];
	int buf4;
}header;

void testStruct(){
	// 初始化Python
    Py_Initialize();
	
	//PyObject *pName = PyBytes_FromString("Test001");PyObject *pModule = PyImport_Import(pName);  //调用Python的Module的另一种方法
    PyObject * pModule = NULL;
	PyObject *pDict = NULL;
	PyObject * pFunc = NULL;

	pModule = PyImport_ImportModule("Test001");
	if (!pModule) {
        printf("can't find Test001.py");
        return ;
    }

    pDict = PyModule_GetDict(pModule);
    if (!pDict) return ;

    //下面这段是查找函数testStruct并执行testStruct
    pFunc = PyDict_GetItemString(pDict, "testStruct");
    if (!pFunc || !PyCallable_Check(pFunc)) {
        printf("can't find function [testStruct]");
        return ;
    }

    //创建结构体
    header input;
    memset(&input,0,sizeof(input));
    input.buf1 = 1;
    input.buf2 = 2;
    input.buf4 = 3;
    strcpy_s(input.buf3, "kjaf");
    
    //打包成string
    char * byInput = new char(sizeof(input));
    memcpy(byInput, &input, sizeof(input));

    //申请python入参
    PyObject *pArgs = PyTuple_New(1);
    //对python入参进行赋值; s代表char*格式, #代表传入指定长度
	PyTuple_SetItem(pArgs, 0, Py_BuildValue("s#",byInput , sizeof(input)));

	//PyObject *Pystructs = Py_BuildValue("s#",byInput, sizeof(input));//为了检验入参是否为空
    //if (Pystructs == nullptr){printf("Pystructs is nullptr"); }
	
	//执行函数
    PyObject *pResult = PyObject_CallObject(pFunc, pArgs);

    char* pRsp;
    //获取返回值
	PyArg_Parse(pResult, "s#", &pRsp);  //由于传回的参数为bytes(经过Python的unpack后,传回的参数),故用"s#",而不是"s"
    //转成结构体
    header* pstRsp = (header*)pRsp;//把string转化为结构体
    printf("\n-----------c++层接收py返回:\nbuf1:%d , buf2:%d , buf3:%s , buf4:%d\n\n", 
		pstRsp->buf1, pstRsp->buf2, pstRsp->buf3, pstRsp->buf4);
	
    Py_Finalize();
}

Python部分:

import struct

def testStruct(a):
  
    a = a.encode('utf-8')#要注意把从C传过来的string转化为bytes,以便struct.unpack解析
    ret = struct.unpack('i i 12s i', a)
    
    print("--------------------python receive c++ struct:\n")
    print(ret)
    buf1 = ret[0] + 1
    #print('buf1: ',buf1)
    buf2 = ret[1] + 1
    buf4 = ret[3] + 1
    print("--------------------begin pack data and begin send to c++\n")
    bin_buf_all = struct.pack('ii12si', buf1, buf2,b"dfds", buf4)  #struct.pack后,bin_buf_all为bytes类型
    print("----------------------------python end-----------------------")
    return bin_buf_all

运行结果:

C调用Python(传递数字、字符串、list数组(一维、二维),结构体)

注:结构体显然就是一种的数据组织方式,使用结构体要注意数据对齐的问题,有兴趣的话可以看一下https://blog.****.net/mirale/article/details/38128709

 

 

参考博文:

混合编程之——C++调用python2.7&python3.5

C++调用Python的API总结

C与python的调用一(导入python模块与,获得函数与类)

Python + C/C++ 嵌入式编程(1):多维数组Numpy.Array()在Python和C/C++文件间的传递问题

c++调用python系列(1): 结构体作为入参及返回结构体

python str与bytes转换

C中结构体和字节流的互换及内存对齐