方法1:ctypes调用c/c++动态链接库(dll或so)
import ctypes
#以下4种方法选一种ok即可
#cdecl调用方法1
dll=ctypes.cdll.LoadLibrary
lib=dll("D:/pyd/MyMath.dll") #MyMath.so
#lib=ctypes.cdll.LoadLibrary("D:/pyd/MyMath.dll")#MyMath.so
#cdecl调用方法2
lib=ctypes.CDLL("D:/pyd/MyMath.dll")#MyMath.so
#stdcall调用方法1
lib=ctypes.windll.LoadLibrary("D:/pyd/MyMath.dll")#MyMath.so
#stdcall调用方法2
lib=ctypes.WinDLL("D:/pyd/MyMath.dll")#MyMath.so
#在调用c方法前可以指定传入参数的类型和返回参数的类型
lib.Add.argtypes = [ctypes.c_double, ctypes.c_double]
lib.Add.restype = ctypes.c_double
print(lib.Add(1.5,4.0))
# print(lib.Add(ctypes.c_double(1.5), ctypes.c_double(4.0)))
注意:
1.c++代码需要按照C语言编译链接,Linux下是
extern "C"
而windows下是
extern "C" {
__declspec(dllexport) double Add(double x, double y);
__declspec(dllexport) double Subtract(double x, double y);
__declspec(dllexport) double Multiply(double x, double y);
__declspec(dllexport) double Divide(double x, double y);
}
2.加载链接库的路径可以是相对路径,不过有时会报错
3.有时结果不一定正确,需要指定输入输出参数的类型,因为ctypes模块在连接python和c/c++时数据类型存在一个映射关系,如下:
方法2:调用C/C++编写的python扩展模块(pyd)
这种方法比较好,用C/C++编写python的扩展模块,在python程序里面import进去就可以调用接口
1.pyd生成。以下是Windows VS处理情况
环境配置:
cpp文件处理如下
// MyMath.cpp
#include "pch.h"
#define ENABLE_PYD 1//0:DLL 1:PYD
#if ENABLE_PYD
#include "Python.h"
#else
extern "C" {
__declspec(dllexport) double Add(double x, double y);
__declspec(dllexport) double Subtract(double x, double y);
__declspec(dllexport) double Multiply(double x, double y);
__declspec(dllexport) double Divide(double x, double y);
}
#endif
double Add(double x, double y)
{
return x + y;
}
double Subtract(double x, double y)
{
return x - y;
}
double Multiply(double x, double y)
{
return x * y;
}
double Divide(double x, double y)
{
return x / y;
}
#if ENABLE_PYD
// 封装c++函数
//1.每个定义的函数都需要一个样板函数
PyObject* MyMath_Add(PyObject* self, PyObject* args)
{
double x, y;
if (!PyArg_ParseTuple(args, "dd", &x, &y))
{
return NULL;
}
return Py_BuildValue("d", Add(x, y));
}
PyObject* MyMath_Subtract(PyObject* self, PyObject* args)
{
double x, y;
if (!PyArg_ParseTuple(args, "dd", &x, &y))
{
return NULL;
}
return Py_BuildValue("d", Subtract(x, y));
}
PyObject* MyMath_Multiply(PyObject* self, PyObject* args)
{
double x, y;
if (!PyArg_ParseTuple(args, "dd", &x, &y))
{
return NULL;
}
return Py_BuildValue("d", Multiply(x, y));
}
PyObject* MyMath_Divide(PyObject* self, PyObject* args)
{
double x, y;
if (!PyArg_ParseTuple(args, "dd", &x, &y))
{
return NULL;
}
return Py_BuildValue("d", Divide(x, y));
}
//2.方法定义-就是该module包含了哪些Methods
static PyMethodDef MyMath_methods[] = {
{"add", MyMath_Add, METH_VARARGS, "This is an add function."},
{"subtract", MyMath_Subtract, METH_VARARGS, "This is a subtract function."},
{"multiply", MyMath_Multiply, METH_VARARGS, "This is a multiply function."},
{"divide", MyMath_Divide, METH_VARARGS, "This is a divide function."},
{NULL, NULL, 0, NULL}
};
//3.模块定义
static PyModuleDef MyMath_module = {
PyModuleDef_HEAD_INIT,
"MyMath",
"My Math Module",
0,
MyMath_methods
};
//4.启动样板函数
PyMODINIT_FUNC PyInit_MyMath()
{
return PyModule_Create(&MyMath_module);
}
#endif
函数介绍:
包裹函数(如MyMath_Add):它负责将Python的参数转化为C的参数(PyArg_ParseTuple),调用实际的Add,并处理Add的返回值,最终返回给Python环境。
参数解析PyArg_ParseTuple:将python的变量解析成C/C++变量,按照ii,si,ss等格式。
参数解析Py_BuildValue():和PyArg_ParseTuple()的作用相反,它是将C类型的数据结构转换成Python对象。
导出表MyMath_methods:它负责告诉Python这个模块里有哪些函数可以被Python调用。导出表的名字可以随便起,每一项有4个参数:第一个参数是提供给Python环境的函数名称,这个名称可以任取,第二个参数是包裹函数。第三个参数的含义是参数变长,第四个参数是一个说明性的字符串。导出表总是以{NULL,NULL, 0,NULL}结束。
导出函数PyInit_MyMath:这个的名字不是任取的,MyMath是module名称。导出函数中将模块名称与导出表进行连接。
2.python文件如下:
import MyMath
print(MyMath.add(1.5, 3.))
print(MyMath.subtract(1.5, 3.))
print(MyMath.multiply(1.5, 3.))
print(MyMath.divide(1.5, 3.))
print(type(MyMath.add(1.5, 3.)))
注意:
编译的pyd模块放在python文件能识别的目录,最好放在同一个目录。
方法3:调用二进制可执行文件
用python程序调用C/C++编译的可执行文件
import os
os.system('test.exe')
方法4:使用SWIG(高级)
这是一个第三方的针对python的扩展包,需要些配置文件,略。
待续。。。