zoukankan      html  css  js  c++  java
  • 使用C/C++写Python模块

    最近看开源项目时学习了一下用C/C++写python模块,顺便把学习进行一下总结,废话少说直接开始:

    环境:windows、python2.78、VS2010或MingW

    1 创建VC工程

    (1) 打开VC6.0或VS2008,然后File-->New-->Project-->Win32 DLL Project。建立一个Empty Project,比如testClass,一路确定。

    (2) 之后向工程添加python头文件目录及库文件目录,如头文件目录:F:python278include,库文件目录:F:python278libs

    (3) 添加一个C++或C源文件,如工程中有用到类,则添加的必须是C++文件,这里直接添加main.cpp

    #include <Python.h>
    #include <iostream>
    #include <sstream>
    #include <structmember.h>
    #include <windows.h>
    
    using namespace std;  
    
    typedef struct _CScore
    {
        PyObject_HEAD
        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}
    };
    
    //////////////////////////////////////////////////////////////
    // 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;                            //先释放其字符指针对象.
        }
        OutputDebugString(TEXT("destroy!!!"));
    
        //如果还有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());
    }
    
    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;
    }
    
    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 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,
    };
    
    ////////////////////////////////////////////////////////////
    // 此模块的说明信息. 由于我用的python2所以直接把该部分进行注释
    //
    /*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
    };*/
    
    int add(int x, int y)
    {
        return x+y;
    }
    
    static PyObject* W_add(PyObject* self, PyObject* args) {
        int x;
        int y;
        if(!PyArg_ParseTuple(args, "i|i", &x, &y)) {
            return NULL;
        } else {
            return Py_BuildValue("i", add(x, y));
        }
    }
    
    static PyMethodDef module_methods[] = {{"add", W_add, METH_VARARGS, "a function from C"},{NULL, NULL, 0, NULL}};
    
    PyMODINIT_FUNC inittestClass(void)       //模块外部名称为--CppClass
    {
        PyObject* pReturn = 0;
        CScore_ClassInfo.tp_new = PyType_GenericNew;       //此类的new内置函数—建立对象.
    
        /////////////////////////////////////////////////////
        // 完成对象类型的初始化—包括添加其继承特性等等。
        // 如果成功,则返回0,否则返回-1并抛出异常.
        if(PyType_Ready(&CScore_ClassInfo) < 0)
            return;
    
        pReturn = Py_InitModule3("testClass", module_methods, "Example module that creates an extension type.");
        if(pReturn == NULL)
            return;
        Py_INCREF(&CScore_ClassInfo);
        PyModule_AddObject(pReturn, "CScore", (PyObject*)&CScore_ClassInfo); //将这个类加入到模块的Dictionary中.
        return;
    }
    main.cpp

    之后修改VC工程导出文件后缀名为pyd

     

    2 使用testClass模块

    编译工程生成testClass.pyd模块文件,进入到导出文件目录,并启动python

    导入testClass模块并查看其详细信息

    模块详细使用

    其他编译方式

    下面介绍另一种用Python Script来生成.pyd文件的方法,新建一个Python脚本——CreatePyd.py,其内容如下:

    from distutils.core import setup, Extension
    ModuleInfo = Extension("testClass", sources = [r"main.cpp"])
    setup(name             = "testClass",
          version          = "1.0",
          description      = "This module created by C++ weiwei.Zhao",
          author           = 'weiwei.zhao',
          author_email     = 'weiwei22844@hotmail.com',
          license          = "You can copy this program to anywhere.",
          url              = "http://zhaoweiwei.top",
          long_description = '''This is really just a demo!''',
          platforms        = "Windows",
          ext_modules      = [ModuleInfo]
         )
    CreatePyd.py

    内容解释:先导入所需的Python模块,然后用Extension函数关联一个Cpp源文件一个要生成的模块名——注意:没有.pyd后缀。然后调用setup函数生成一个名字name为testClass的模块,版本version为1.0,描述description,作者信息author,作者邮箱author_email,还其平台platforms等等有用的信息!

    最后调用:python CreateDLL.py build

    经过编译后,就会新生成一个build目录,在build/lib.win32-2.7下你可以找到testClass.pyd文件。

    需要注意的是编译脚本默认使用VS编译器,所以电脑上要安装VS,如果电脑上没有安装VS而是有MingW环境,则类似的可以使用MingW环境中gcc进行编译:

    注意:为了使相关程序都能顺利找见,上图是在MingW的Shell命令行,而不是普通的windows Command命令行。

    4 模块部署安装

    一般来说,setup.py参数说明

    #python setup.py build      # 编译

    #python setup.py install     # 安装

    #python setup.py sdist      # 生成压缩包(zip/tar.gz)

    #python setup.py bdist_wininst   #生成NT平台安装包(.exe)

    #python setup.py bdist_rpm  #生成rpm包

    或者直接"bdist 包格式",格式如下:

    #python setup.py bdist --help-formats

       --formats=rpm       RPM distribution

       --formats=gztar     gzip'ed tar file

       --formats=bztar     bzip2'ed tar file

       --formats=ztar     compressed tar file

       --formats=tar       tar file

       --formats=wininst   Windows executable installer

       --formats=zip       ZIP file

    下图说明了windows安装程序生成过程,在dist目录下有windows的安装程序testClass-1.0.win32-py2.7.exe

    需要说明的是如果生成的testClass.pyd依赖于其他库文件,如在我的环境下用MingW生成的testClass.pyd要依赖libgcc_s_dw2-1.dll和libstdc++-6.dll两个文件,生成安装包时需把这两个文件和testClass.pyd放到一起,如放到build/lib.win32-2.7目录下,之后生成的安装文件testClass-1.0.win32-py2.7.exe会包含着两个文件,如果不这样做在导入testClass时会导致导入失败,提示类似:

    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    ImportError: DLL load failed: 找不到指定的程序。

    结束语

    以上的总结参考了部分网友的博文,主要如下,一并感谢:

    http://blog.csdn.net/arnozhang12/article/details/5409155

    http://blog.csdn.net/carolzhang8406/article/details/6925745

  • 相关阅读:
    HDU5772 (最小割)
    HDU 4971 (最小割)
    暑期集训个人赛1
    HDU 5644 (费用流)
    HDU5619 (费用流)
    暑假集训热身赛
    构建之法阅读笔记05
    找小水王
    找水王
    Runner站立会议之个人会议(冲刺二)
  • 原文地址:https://www.cnblogs.com/zhaoweiwei/p/pythonModule.html
Copyright © 2011-2022 走看看