- 浏览: 11402 次
最新评论
C++导出类到Python
2011年03月24日
C++导出类到Python
点击下载源码
上次稍微谈了下如何用C++写的函数来扩展Python,使Python很方便的调用用C++写的高效的、快速的函数。我们知道--Python是面向对象的,那么我们是否可以用C++写的类来扩展Python程序呢?答案是可以的!首先说明:我的Python是3.1的,与2.x在一些Python/C API有区别。 一、简介 :
比如我们有一个C++类--用于记录一个学生的信息:姓名、数学成绩、英语成绩,呃,就这些吧!也许你会说这不是没事找事么?Python也可以很简单、很快的实现这个类。但我要说的是--我只是想用这个小例子来阐述"用C++类扩展Python"的主要思想,达到举一反三的效果。只要学会了这个例子,就可以将任意复杂的类导出到Python环境供Python调用。
这个类的大致雏形如下,用C++:
view plain copy to clipboard print ?
.........10........20........30........40........5 0........60........70........80........90........10 0.......110.......120.......130.......140.......150
class CScore
{
public:
CScore();
~CScore();
//一些成员函数的定义处...
private:
char* m_szName;
float m_fMath;
float m_fEnglish;
}; 用Python类写这个类:
也很简单,就是这些成员函数的定义也大致相同。如果你能看懂上面两个不同语言定义的类,那么请继续!不然可能有些吃力。下面我们来实现这样一个操作:"用C++语言写这样一个CScore类,可以在Python Intepreter中方便的调用"。 OK,Let's Go!
二、具体操作:
1> 依然新建一个Win32 DLL Project,添加一个Cpp源文件--比如是Extension_Class.cpp,保证你的Python的.../Include ../Libs路径是添加到VC系统路径中的。然后逐一添加以下代码:
Python.h很熟悉,是不?structmember.h又是一个新的头文件,需要用到里面的数据结构。其余不解释!
2> 也许你会很自然的直接写一个C++类,但我们会发现,事实并不是这样。接着来:
////////////////////////////////////////////////// //////////
// 类/结构的定义.
typedef struct _CScore
{
PyObject_HEAD // == PyObject ob_base; 定义一个PyObject对象.
char* m_szName;
float m_dMath;
float m_dEnglish;
}CScore;
可以看到我们定义了一个结构而并不是一个类。第一个PyObject_HEAD是一个宏,我们可以在Python的Include中object.h头文件找到它的定义:
大意是PyObject_HEAD是每一个PyObject的初始段。其余的成员依次是学生的名字(m_szName)、数学成绩(m_fMath)、英语成绩(m_fEnglish)。
3> 有了类的定义还不够,还得对它的数据成员进行说明:
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."},
{"m_dTotal", T_FLOAT, offsetof(CScore, m_dTotal), 0, "The Total score of instance.align"},
{NULL, NULL, NULL, 0, NULL}
;
①PyMemberDef是类成员的说明结构。我只解释其中一个成员--m_szName,其余的一样。先来这个字符串"m_szName"就是导出到Python中的类成员,这个名字不一定非得和C++中相应类成员的名字相同。
②T_STRING、T_FLOAT是这个成员的数据类型的说明。不解释!
③offsetof亦是一个宏,在STDDEF.h中:
#define offsetof(s,m) (size_t)&(((s *)0)->m) 那么offsetof(CScore, m_szName)就是: (size_t)&(((CScore*)0)->m_szName)。
④下来是一个字符串"The …"--这是成员的DocString。其余成员依次类推。完了!
4> 我们知道,作为一个类,光有数据时不行的,还得有方法可供调用。下面来给这个类添加方法:
①先看类的构造、析构等内置方法:
////////////////////////////////////////////////// ////////////
// 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))
{
coutm_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;
OStrm_szNamem_dMathm_dEnglishPython中类方法定义时的self一样,和C++中的this指针的机理差不多。正如你所见,用它来引用类中的数据成员。
在构造方法中,我们通过解析参数,得到每一个成员的值。由于在构造方法中new了m_szName的内存空间,所以在析构函数中释放它。如果你看过我的上一篇《用C++扩展Python》的话,这几个函数理解起来应该很容易!
②CScore类的外部方法:
////////////////////////////////////////////////// //////////
// 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))
{
coutm_dEnglish))
{
coutm_szNamem_dMathm_dEnglish 类的所有方法的列表说明: 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}
};
每一项都唯一的说明了一个类方法。下面解释之:
① 依然是static,PyMethodDef定义了方法列表说明的结构。第一个字符串"GetName"--是Python中可见的那个函数名--依然不必与C++中所对应的函数名相同,可以自己定。
②(PyCFunction)CScore_GetName--是C++中的对应的函数指针。
③METH_VARARGS--定义了从Python向C++函数传递参数的方法。METH_VARARGS指定了用Tuple传递,如果没有参数,请用METH_NOARGS。
④一个字符串"Get the Name …Instance"--是这个函数的DocString,在Python中可以用MethodName.__doc__获得。
6> Yeah!类的各个部件--数据成员、方法成员都已说明完毕!下面就是将这些说明列表有机的组织起来。如下:
////////////////////////////////////////////////// //////////
// 类/结构的所有成员、内置属性的说明信息.
//
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, //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,
};
不知你此刻感觉如何,我当初是被吓着了!不过回头看一下,也挺简单,一一介绍之。
①依然static!第一个字符串可以用ClassName.__name__获取,是类的内置名称。
②sizeof(CScore)--说明了类/结构的大小。
③有0的不看了,其余的有注释,其中每个位置的对应项不可搞错。比如第4项是析构函数的函数指针,第27、28项分别说明了类的所有数据、方法集合。35项是类的构造函数……
7> 一切对类的说明完毕,下来是对模块的说明:
////////////////////////////////////////////////// //////////
// 此模块的说明信息. //
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
};
①依然static!第一项总为PyModuleDef_HEAD_INIT。
②模块的内置名,可以用ModuleName.__name__获取。
③模块的DocString,可以用ModuleName.__doc__获取。
8> 好了!终于到了导出这一步了。导出函数的名字和模块的名字息息相关。
基本上有:模块初始函数名 = PyInit_ + 模块文件名. 这是Python 3.1中的方法,2.x中可能有出入!
////////////////////////////////////////////////// //////////
// 模块的初始化函数. 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) Python Intepreter中的名字--这个字符串依然可以和C++中所对应的类名不同!
三、测试导出模块:
激动人心的时候到了!我们的类究竟是否在Python中可用?下面就是--"见证奇迹的时刻!" 1> 我们很熟练的设置Win32 DLL Project生成的文件为Score.pyd--因为我们的初始函数为PyInit_Score!如果你还不知道如何设置,请参考我的《用C++扩展Python》一文。
2> 然后组建即可。在Release目录下,不出意外的话可以找到一个120多KB的Score.pyd文件。我们可以将它复制到D:\下,然后cmd、cd切换到D:\下,键入Python,得到如下界面:
3> 然后可以随心所欲的进行操作了,比如我们键入下列命令: >>>import Score >>>dir(Score) >>>Score.__doc__ >>>Score.__name__ >>>Score.__file__ 可以依次得到模块Score的DocString、内置模块名、模块所对应的文件名……运行结果如下:
4> 有待探索的东西还很多:
>>>from Score import * #导入模块中所有方法
>>>dir(CScore) #查看类CScore的所有内置属性
>>>CScore.__doc__
>>>CScore.GetEnglish.__doc__
>>>CScore.GetMath.__name__
>>>CScore.GetName 可以看到,输出的信息和我们所定义的是一致的!有一点成就感了吧?
5> 下面是重中之重--我们的类到底可不可用呢?试试先:
>>>StuScore = CScore("Kobe Bryant", 88, 100)
>>>StuScore.GetName()
>>>StuScore.GetMath()
>>>StuScore.GetEnglish()
>>>StuScore.PrintInfo()
这一幕终于出现了!!!我们看到,我们的类的确起了作用,并且工作的很好!不是吗?我们可以获得他的各种信息,也可以将其信息打包输出。当然,还可以更改:
>>>StuScore.SetMath(90.5)
>>>StuScore.SetEnglish(95)
>>>print(StuScore)
一切皆在预料之中!我们甚至可以用Python中内置的print函数输出这个类!!!请不要惊慌,我们的print函数只是简单的调用了print(str(StuScore))这个函数而已!而对内置函数str的定义是我们在C++程序中所作了的--还记得那一长串令人头痛的类信息说明结构吗??
当del时,对象自动调用C++中所定义的那个析构函数来释放所占用的内存!
6> 这个模块中还有一些东西有待挖掘,我想,还是留给您去探索吧--这样才会了解的深入。
通过这个例子我们可以看出,我们可以很方便的用C++写的类来扩展Python库!也许你不认为这是个好办法--Python自身写的类就可以啊,为什么非要用C++写的呢?但请不要忘记--在对系统处理速度要求较高的一些软件(或程序)中,可以很方便的利用C/C++的运行速度快的特点,来对Python进行扩展。
7> 呃,就这样完了?不会!如果你对空间大小要求较高的话,可以看到--就这么几行的一个类,却要占用120多KB的空间,如果是一个几百行的类呢?一些类呢?下面介绍一种用Python Script来生成.pyd文件的方法。准备好了吗?Go!
①我们新建一个Python脚本--CreatePyd.py。然后进行如下编辑: from distutils.core import setup, Extension
ModuleInfo = Extension("Score", sources = [r"Extension_Class.cpp"])
setup(name = "Score",
version = "1.0",
description = "This module created by C++ YaFeng.Zhang",
author = 'YaFeng.Zhang',
author_email = 'zyfgood12@163.com',
license = "You can copy this program to anywhere.",
url = "http://blog.csdn.net/ZhangYafengCPP",
long_description = '''''This is really just a demo!''',
platforms = "AMD5600+ WinXP",
ext_modules = [ModuleInfo]
)
我们大致解释下:先导入所需的Python模块。然后用Extension函数关联一个Cpp源文件(就是我们刚刚写的那个文件)和一个要生成的模块名--注意:没有.pyd后缀。
然后调用setup函数生成一个名字name为Score的模块,版本version为1.0,描述description,作者信息author,作者邮箱author_email,还其平台platforms等等他有用的信息!
比如我们将这个CreateDLL.py和Extension_Class.cpp拷贝到D:\下,依然切换到D:\目录下,键入"python CreateDLL.py build":
经过生成后,就会新生成一个build目录,在build\lib.win32-3.1下你可以找到Score.pyd这个文件。但它只有12KB!比用VC生成的整整小了10倍!可以试一下,它依然能正常工作。
8> 模块的安装:
也许你对你生成的模块比较满意,那么你可以将其拷入Python的lib目录下,以后不用切换盘符就可以方便的调用。但还有一种更为简便的方法:用命令行键入: Python CreateDLL.py install:
也许你的显示和我不同--因为我们的Python不一定安装在同一个目录下。但结果是我们在Python安装目录下的lib/site-packates下可以找到刚刚生成的.pyd模块和一个模块信息说明文件,我们用记事本打开之:
其中恰恰是我们的py脚本中给setup函数所传递的那些参数!
四、小结:
好了,就这些。我们通过"创建结构--> 创建方法--> 数据成员说明-->方法说明--> 关联结构和数据、方法--> 模块说明--> 生成模块"这一系列犀利的操作成功的从C++中向Python导出了一个类!
还简单的介绍了如何用Python生成模块、以及模块的简便安装等等!好了,就这些吧!结合上一篇《用 C++扩展Python》和这篇《C++导出类到Python》,我已经基本上将如何用C++扩展Python模块将述了一遍。希望对你有所帮助,至少对于我而言,是总结了我的所学!
如果你有什么建议或想法,请与我联系,让我们共同进步!我的联系方式:
Blog: http://blog.csdn.net/ZhangYafengCPP
Email: zyfgood12@163.com
QQ: 798446303
--By: Ac_1ZYF-C++
发表评论
-
python学习笔记
2012-01-20 09:38 634python学习笔记 2010年07月06日 个人学习p ... -
Python学习笔记
2012-01-20 09:38 652Python学习笔记 2010年09月13日 Pytho ... -
Python GUI编程各种实现的对比
2012-01-20 09:38 1033Python GUI编程各种实现的 ... -
Python编程简介
2012-01-20 09:38 1022Python编程简介 2011年06 ... -
2011年第十届中国机械(越南)展览会
2012-01-19 14:41 4612011年第十届中国机械(越南)展览会 2010年11月22 ... -
去越南相亲吧~~~~~
2012-01-19 14:41 505去越南相亲吧~~~~~ 2011年05月04日 越南,一 ... -
越南攻略
2012-01-19 14:41 480越南攻略 2010年07月02日 ... -
常用JavaScript大全
2012-01-17 04:27 714常用JavaScript大全 2010年11月11日 1 ... -
【总结】Java EE知识点(持续更新)
2012-01-17 04:27 945【总结】Java EE知识点( ... -
Javascript 基本函数和技巧
2012-01-17 04:27 592Javascript 基本函数和技巧 2010年09月22日 ... -
JavaScript函数大全
2012-01-17 04:26 521JavaScript函数大全 2011年02月12日 1 ... -
js中的常用的几个对象
2012-01-17 04:26 556js中的常用的几个对象 2010年07月01日 jav ... -
初三语文总复习
2012-01-16 03:16 635初三语文总复习 2009年1 ... -
女人智慧,经典十课
2012-01-16 03:16 750女人智慧,经典十课 2010 ... -
连锁宾馆、酒店培训
2012-01-16 03:16 526连锁宾馆、酒店培训 2011年06月15日 (前厅部) ...
相关推荐
这个例子示范了如何从C++中导出一个类到Python。轻松的扩展Python模块!
首先,将C++下的代码编译为动态库,并将生成的动态库命名为封装模块的名字,如:用BOOST_PYTHON_MODULE(Module_Name)宏对需要导出的函数、全局变量、类等导入Python的Module_Name模块,此时生成的动态库需要更名为...
Python中调用C++dll例子,使用python中的ctypes。
自定义的dll中包含自定义类,如何从dll中到处自定义类,具体查看源代码
包括C++、Java、python、web、C#、EDA等项目的源码。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的...
除支持自身的工程文件、C/C++文件外,还支持AngelScript、批处理、CSS文件、D语言文件、Diff/Patch文件、Fortan77文件、GameMonkey脚本文件、Hitachi汇编文件、Lua文件、MASM汇编文件、Mathlab文件、NSIS开源安装...
通过用C ++编写对计算有要求的部分,通过Pybind11将许多C ++类导出到Python,以及灵活的Python驱动程序,Psi4致力于对用户和开发人员友好。 用户网站 下载和安装Psi4 (有关CMake熟练者,请参阅 手册 (从主分支...
自测此Excel类导出200000行数据(58个字段)正常完成。一般DotNet实用Oledb读取Excel的时候,经常出现数据类型错误,导致无效结果。比如Excel中编码列,编码:00001225 读取到Datagridview中可能变成 1224数字类型了...
3. 含训练和测试数据,含训练ok的pth模型和ONNX模型,含完整python和C++实现; 4. 使用方法:首先运行“TrainTestConvertOnnx.py”执行“训练数据读入、模型训练、模型测试、导出onnx”,再运行“TestOnnx.cpp”...
C、C++、C#、Rust、Python、Java 和 JavaScript 数组 ASCII-Art 十六进制视图 HTML 自包含 div 字符串和十六进制搜索 多彩高亮 从开始、结束和当前光标位置转到 用于解析突出显示文件内容的自定义类 C++ 模式语言 ...
从 TXT 或 CSV 文件中读取数据,用模拟退火算法搜索找到解决回归和...- 将解导出为 Python、C/C++ 或纯文本。 - 多任务处理。 - 使用低级编程语言(非 Python)编写,速度极快。 - 命令行模式,可让您自动化运行程序。
CCEXP(C类EXPort)是C ++中的一个库,基于模板以及标准C / C ++库和函数,用于有组织的数据提取和C ++代码与其他软件(例如MatLab / Octave,Python等)之间的共享。 目前,共享是通过文件完成的,文件可以存储大量...
软件开发设计:PHP、QT、应用软件开发、系统软件开发、移动应用开发、网站开发C++、Java、python、web、C#等语言的项目开发与学习资料 硬件与设备:单片机、EDA、proteus、RTOS、包括计算机硬件、服务器、网络设备、...
除支持自身的工程文件、C/C++文件外,还支持AngelScript、批处理、CSS文件、D语言文件、Diff/Patch文件、Fortan77文件、GameMonkey脚本文件、 Hitachi汇编文件、Lua文件、MASM汇编文件、Mathlab文件、NSIS开源安装...
VideoProcessingFramework VPF代表视频处理框架。 它是一组C ++库和Python绑定,可为视频处理任务提供完整的硬件加速,... VPF还支持将诸如解码视频帧之类的GPU内存对象导出到PyTorch张量,而无需“主机到设备”副本。
它目前支持类层次结构的代码生成、类上下文中的状态机以及类操作上下文中的活动图。 对于状态机触发器,目前仅支持Call Events和Time Events 。 当时,Genna 并未验证输入模型的正确性。 代码生成的目标语言是 C++ ...
训练所需的模型是通过一个简单的Python脚本创建和导出的,该脚本使用自定义覆盖的预测和训练功能编译图形结构,然后将其导出为一种格式,可以通过C ++代码轻松访问和修改权重和结构。 培训允许自定义批次大小,...
自定义C++类模式语言,支持对文件内容进行解析和高亮显示。数据导入:支持Base64文件、IPS和IPS32。数据导出:IPS和IPS32。数据检查器允许解释多种不同类型的数据(小端和大端)。大文件支持和快速有效的加载。文件...
自定义C++类模式语言,支持对文件内容进行解析和高亮显示。 数据导入:支持Base64文件、IPS和IPS32。 数据导出:IPS和IPS32。 数据检查器允许解释多种不同类型的数据(小端和大端)。 大文件支持和快速有效的...
6502,8088,Basic,DBase,Z-80,TP801,Foxbase,Foxpro,VB,boland C,Access,Fortran,C-Base,Delphi,C++,SQL,Java,Action script, JScript, VBScript, C#,Dot Net, Python, Erlang 欧阳俊曦是广州人,生肖属鼠,天秤星座...