- 比C/C++调用Python更有意义:先用 Python 快速开发出大部分功能,需要性能优化的部分再优化到C/C++
- 基本思路是,将C/C++封装为动态链接库(windows下为 .pyd,linux下为 .so)供 Python 调用
- 将分4篇介绍Python扩展的方法、Cython方法、SWIG方法、ctypes方法
Python 扩展方法
封装若干函数到动态链接库
1)代码
#include "stdafx.h"
#include "iostream"
#include "Python.h"
using namespace std;
int add_it(int a, int b){
int c = a+b;
cout << "【C/C++】" << a << " + " << b << " = "<<c<<endl;
return c;
}
int sub_it(int a, int b){
int c = a-b;
cout << "【C/C++】" << a << " - " << b << " = "<<c<<endl;
return c;
}
// 包裹函数,实现 Python 参数类型到 C/C++ 参数类型的转换
static PyObject* _add_it(PyObject* self, PyObject* args){
int _a, _b;
int res;
if(!PyArg_ParseTuple(args, "ii", &_a, &_b))
return 0;
res = add_it(_a, _b);
return PyLong_FromLong(res);
}
static PyObject* _sub_it(PyObject* self, PyObject* args){
int _a, _b;
int res;
if(!PyArg_ParseTuple(args, "ii", &_a, &_b))
return 0;
res = sub_it(_a, _b);
return PyLong_FromLong(res);
}
// 负责告诉 Python 这个模块里有哪些函数可以被调用
// 每一项有 4 个参数
static PyMethodDef ModuleMethods[] = {
{
"add_it", // Python 环境下的函数名称
_add_it, // 包裹函数
METH_VARARGS, // 参数形式,将所有参数以元组方式打包
"" // 说明
},
{
"sub_it",
_sub_it,
METH_VARARGS,
""
},
{NULL, NULL, 0, NULL}
};
// 导出函数将模块名称与导出表连接
// c_module 是导出链接库的名字,对应导出函数名 initXXX
PyMODINIT_FUNC initc_module(void){
(void) Py_InitModule("c_module", ModuleMethods);
}
2)编译环境配置
Windows
以VS2010为例,添加 Python 的 include 和 lib
Ubuntu
有2点需要注意:1. 默认导出名字会在前面加 lib ,所以要修改 initc_module 函数名字
2.initc_module 前加 extern “C”
extern "C" PyMODINIT_FUNC initlibc_module(void){
(void) Py_InitModule("libc_module", ModuleMethods);
}
.pro 文件
QT += core
QT -= gui
TARGET = c_module
TEMPLATE = lib
INCLUDEPATH += /usr/include/python2.7\
/usr/include/x86_64-linux-gnu/python2.7\
-fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes
LIBS += -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7\
-lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
SOURCES += main.cpp
3)Python 端直接 import 模块
封装类到动态链接库
重点参考文章《C++导出类到Python》
从底层到顶层包括以下部分
- 类(以结构方式)数据成员定义及说明
- 类所有方法(包括构造和析构)的定义及说明
- 类的说明
- 模块的说明
- 模块的初始化函数(import时自动调用)
以下代码摘自文章《C++导出类到Python》
实现一个 CScore 类,记录名字、数学、英语成绩
#include <Python.h>
#include <structmember.h> // Python/include/structmember.h
1)数据成员定义及声明
//////////////////////////////////////////////////////////////////////////
// 类/结构的定义.
//
typedef struct _CScore
{
PyObject_HEAD // == PyObject ob_base; 定义一个PyObject对象.
//////////////////////////////////////////////////////////////////////////
// 类/结构的真正成员部分.
//
char* m_szName;
float m_dMath;
float m_dEnglish;
}CScore;
static PyMemberDef CScore_DataMembers[] = //类/结构的数据成员的说明.
{
{"m_szName", T_STRING, offsetof(CScore, m_szName), 0, "The Name of instance"},
{"m_dMath", T_FLOAT, offsetof(CScore, m_dMath), 0, "The Math score of instance."},
{"m_dEnglish", T_FLOAT, offsetof(CScore, m_dEnglish), 0, "The English score of instance."},
{NULL, NULL, NULL, 0, NULL}
};
2)类所有方法定义
//////////////////////////////////////////////////////////////////////////
// CScore类的所有内置、构造方法.
//
static void CScore_init(CScore* Self, PyObject* pArgs) //构造方法.
{
const char* Name = 0;
if(!PyArg_ParseTuple(pArgs, "sff", &Name, &Self->m_dMath, &Self->m_dEnglish))
{
cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
return ;
}
Self->m_szName = new char[strlen(Name) + 1];
strcpy(Self->m_szName, Name);
}
static void CScore_Destruct(CScore* Self)
{
if(Self->m_szName)
delete [] Self->m_szName; //先释放其字符指针对象.
//如果还有PyObject*成员的话,要一并释放之.
//如:Py_XDECREF(Self->Member);
Py_TYPE(Self)->tp_free((PyObject*)Self); //释放对象/实例.
}
static PyObject* CScore_Str(CScore* Self) //调用str/print时自动调用此函数.
{
ostringstream OStr;
OStr<<"Name : "<<Self->m_szName<<endl
<<"Math : "<<Self->m_dMath<<endl
<<"English : "<<Self->m_dEnglish<<endl;
string Str = OStr.str();
return Py_BuildValue("s", Str.c_str());
}
// repr(object) 将python对象转为字符串形式
static PyObject* CScore_Repr(CScore* Self) //调用repr内置函数时自动调用.
{
return CScore_Str(Self);
}
//////////////////////////////////////////////////////////////////////////
// CScore类的所有Get方法.
//
static PyObject* CScore_GetName(CScore* Self)
{
return Py_BuildValue("s", Self->m_szName);
}
static PyObject* CScore_GetMath(CScore* Self)
{
return Py_BuildValue("f", Self->m_dMath);
}
static PyObject* CScore_GetEnglish(CScore* Self)
{
return Py_BuildValue("f", Self->m_dEnglish);
}
//////////////////////////////////////////////////////////////////////////
// CScore类的所有Set方法.
//
static PyObject* CScore_SetMath(CScore* Self, PyObject* Argvs)
{
Py_INCREF(Py_None);
if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dMath))
{
cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
return Py_None;
}
return Py_None;
}
static PyObject* CScore_SetEnglish(CScore* Self, PyObject* Argvs)
{
Py_INCREF(Py_None);
if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dEnglish))
{
cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
return Py_None;
}
return Py_None;
}
static PyObject* CScore_PrintInfo(CScore* Self) //输出平均分.
{
cout<<"The scores as follows:"<<endl
<<"=============================="<<endl
<<"Name : "<<Self->m_szName<<endl
<<"Math : "<<Self->m_dMath<<endl
<<"English : "<<Self->m_dEnglish<<endl
<<"=============================="<<endl;
Py_XINCREF(Py_None);
return Py_None;
}
3)类方法的列表说明
static PyMethodDef CScore_MethodMembers[] = //类的所有成员函数结构列表.
{
{"GetName", (PyCFunction)CScore_GetName, METH_NOARGS, "Get the name of instance."},
{"GetMath", (PyCFunction)CScore_GetMath, METH_NOARGS, "Get the math score of instance."},
{"GetEnglish", (PyCFunction)CScore_GetEnglish, METH_NOARGS, "Get the english score of isntance."},
{"SetMath", (PyCFunction)CScore_SetMath, METH_VARARGS, "Set the math score of instance."},
{"SetEnglish", (PyCFunction)CScore_SetEnglish, METH_VARARGS, "Set the english of instance."},
{"PrintInfo", (PyCFunction)CScore_PrintInfo, METH_NOARGS, "Print all information of instance."},
{NULL, NULL, NULL, NULL}
};
4)类数据及方法的说明
//////////////////////////////////////////////////////////////////////////
// 类/结构的所有成员、内置属性的说明信息.
//
static PyTypeObject CScore_ClassInfo =
{
PyVarObject_HEAD_INIT(NULL, 0)"Module.MyCppClass", //可以通过__class__获得这个字符串. CPP可以用类.__name__获取.
sizeof(CScore), //类/结构的长度.调用PyObject_New时需要知道其大小.
0,
(destructor)CScore_Destruct, //类的析构函数.
0,
0,
0,
0,
(reprfunc)CScore_Repr,
0,
0,
0,
0,
0,
(reprfunc)CScore_Str, //Str/print内置函数调用.
0,
0,
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //如果没有提供方法的话,为Py_TPFLAGS_DEFAULE
"MyCppClass Objects---Extensioned by C++!", //__doc__,类/结构的DocString.
0,
0,
0,
0,
0,
0,
CScore_MethodMembers, //类的所有方法集合.
CScore_DataMembers, //类的所有数据成员集合.
0,
0,
0,
0,
0,
0,
(initproc)CScore_init, //类的构造函数.
0,
};
5)模块的说明及初始化
这里 Python3.X 和 Python2.X 有差异
模块名称为 Score 里面包含类 CScore
// for python3.x
//////////////////////////////////////////////////////////////////////////
// 此模块的说明信息.
// python2.x 里没有 PyModuleDef 及 PyModuleDef_HEAD_INIT 的定义
static PyModuleDef ModuleInfo =
{
PyModuleDef_HEAD_INIT,
"My C++ Class Module", //模块的内置名--__name__.
"This Module Created By C++--extension a class to Python!", //模块的DocString.__doc__
-1,
NULL, NULL, NULL, NULL, NULL
};
//////////////////////////////////////////////////////////////////////////
// 模块的初始化函数. import 时自动调用.
//
PyMODINIT_FUNC // == __decslpec(dllexport) PyObject*, 定义导出函数.
PyInit_Score(void) //模块外部名称为--CppClass
{
PyObject* pReturn = 0;
CScore_ClassInfo.tp_new = PyType_GenericNew; //此类的new内置函数—建立对象.
//////////////////////////////////////////////////////////////////////////
// 完成对象类型的初始化—包括添加其继承特性等等。
// 如果成功,则返回0,否则返回-1并抛出异常.
//
if(PyType_Ready(&CScore_ClassInfo) < 0)
return NULL;
pReturn = PyModule_Create(&ModuleInfo); //根据模块信息创建模块.
if(pReturn == 0)
return NULL;
Py_INCREF(&ModuleInfo);
PyModule_AddObject(pReturn, "CScore", (PyObject*)&CScore_ClassInfo); //将这个类加入到模块的Dictionary中.
return pReturn;
}
// for python2.x
PyMODINIT_FUNC // == __decslpec(dllexport) PyObject*, 定义导出函数.
initScore(void) //模块外部名称为--CppClass
{
PyObject* pReturn = 0;
CScore_ClassInfo.tp_new = PyType_GenericNew; //此类的new内置函数—建立对象.
//////////////////////////////////////////////////////////////////////////
// 完成对象类型的初始化—包括添加其继承特性等等。
//
if(PyType_Ready(&CScore_ClassInfo) < 0)
return;
//根据模块信息创建模块.
pReturn = Py_InitModule("Score", CScore_MethodMembers);
if(pReturn == 0)
return;
PyModule_AddObject(pReturn, "CScore", (PyObject*)&CScore_ClassInfo); //将这个类加入到模块的Dictionary中.
return;
}
其他
- 编译环境与上一节一样
- 有一个比较麻烦的“引用计数”的问题(Py_INCREF() 和 Py_DECREF()),处理不当会引起内存泄露
参考文章《Python引用计数(Reference Count)》,留个坑以后慢慢填