设为首页 收藏本站
查看: 528|回复: 0

[经验分享] Boost.Python学习笔记

[复制链接]

尚未签到

发表于 2017-4-23 09:24:17 | 显示全部楼层 |阅读模式
Python 与 C++ 的交互编程



基础知识

编译语言和解释语言
尽管现在很多编程技术都在交融,出现了不少带有编译系统的解释语言,和带有复杂运行时系统的编译语言,但通常我们还是可以比较明确的区分它们。在这个大前提下,Python和C++属于这个两类技术中比较极端的代表。
编译时和运行时
C++的强大之处在于高效的生成产品和强大复杂的编译系统。利用C++的语法,可以生成非常灵活强大的程序,而这其中大部分工作都是在编译程序的过程中完成。相对而言,C++没有标准的运行时(CLI系统还没有真正成为C++的标准,只能说提供了一个C++在CLI系统上运行的标准),要建立一个完善的运行时,要么就要投入大量的成本,要么就寻找一个合适的运行时环境调用。Python自身是一个成熟完备的脚本语言,拥有开放的编程和扩展接口,加上它本身的对象机制设计非常适合外部调用,Python与编译语言,特别是C/C++的互嵌入日益成为关注焦点。 互嵌入
Python有多种实现,比较著名的有C语言编写的官方实现,Java版的Jython,.net版的IronPython,值得一提的是,后两者都是由 Jim Hugunin 开发,此人现在微软主持动态语言研究项目。另外,有一个CPython调用.net的桥接系统 PythonDotNet,由ZOPE社区的一位技术人员开发。现在使用最广泛的互嵌入场合,是C/C++和CPython之间的互嵌入。
Python 的官方版本本身就是由C语言编写,有开放的C接口。使用C语言调用和扩展Python都非常简单。基于这个前提,使用C++语言与Python进行互操作也并不复杂。现在已经有若干用于Python API封装的C++库,这里我们要讨论的是Boost.Python。这是著名的C++库Boost的重要组成之一。 用C++扩展Python

Python扩展入门
Python允许在遵循一定规则的前提下调用语言和操作系统无关的动态链接库。通常,在Windows下为.dll,而在*nix下为.so文件。操作系统的区别由C++编译器、Boost库和Python解释器屏蔽,最终程序员可以基本忽视其中的区别。
在实现互嵌入的时候,要考虑Python的运行时限制,Python的虚拟机只能操作Python对象,并且虚拟机拥有自己的内存管理模式,应该根据情况对C++程序和Python环境所交换的信息进行必要的封装和拆封。
Python本身提供了C语言扩展所需要的接口文件,可以在Python的include,Libs,DLLs目录下找到对应的文件。
一个纯C语言的Python扩展可能类似下面这样:
#include <Python.h>

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}



  我们根据具体的操作系统和开发工具,把以上函数封装到一个动态链接库中,放入Python的动态链接库搜索路径(Python For Windows中是DLLs)中。就可以把它作为Python的一个模块使用。

C++封装
我们知道,C++的代码封装机制和C有所不同,相比真正的纯C语言,C++更为复杂。例如,它有虚函数,有模版,前者使运行时更为复杂和灵活,后者使一些比宏更复杂的语法推导得以实现。Boost.Python为C++程序提供的封装能力,正是基于template。我们要做的,是把C++程序中的函数、类、数据成员等等,都变成Python对象。
BOOST.Python封装了各平台的Python动态链接库接口,我们要做的只是调用它,封装需要暴露的定义,然后指示编译器生成动态链接库即可。 函数封装
Python的API使用回调函数调用Python对象和函数。BOOST提供完整的函数封装过程,一个简单的封装过程如下:

Hello World
  Following C/C++ tradition, let's start with the "hello, world". A C++ Function:

char const* greet()
{
   return "hello, world";
}
  can be exposed to Python by writing a Boost.Python wrapper:

#include <boost/python.hpp>
using namespace boost::python;

BOOST_PYTHON_MODULE(hello)
{
    def("greet", greet);
}
  That's it. We're done. We can now build this as a shared library. The resulting DLL is now visible to Python. Here's a sample Python session:

>>> import hello
>>> print hello.greet()
hello, world
  

以上来自BOOST文档。
面向对象

对象管理、引用计数
Python虚拟机提供引用计数和自动垃圾回收能力,但是C++对象没有这样的能力(C++对象运行在Python虚拟机之外)。为了避免程序运行过程中的内存使用问题,需要为函数中传递的指针提供引用计数管理,BOOST通过“调用协议”来支持此功能,详情请见BOOST文档“Call Policies”。 C++ 对象到 Python 对象的封装
BOOST文档中提供了C++对象封装至Python对象的方法,主要手段是以class_模板将C++类定义解析为符合C API的形式,一个形如:

struct World
{
    void set(std::string msg) { this->msg = msg; }
    std::string greet() { return msg; }
    std::string msg;
};的C++代码可以通过如下方式封装:

#include <boost/python.hpp>
using namespace boost::python;

BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World")
        .def("greet", &World::greet)
        .def("set", &World::set)
    ;
}这里的关键点在于封装代码必有的 BOOST_PYTHON_MODULE 宏和class<>模板的def。为了支持C++类定义丰富的内容,Boost.Python提供了很多调用方法,比较常用,也比较重要的是构造函数、重载和虚函数的支持。
构造函数的封装是通过__init__模板封装的,不同的构造函数重载可以通过参数列表区分并重复封装。BOOST文档中给出了这样一个示例:
class_<World>("World", init<std::string>())
    .def(init<double, double>())
    .def("greet", &World::greet)
    .def("set", &World::set)
;
普通函数重载的支持事实上是一种函数封装方法,和这个函数是否是某个类的成员并无关系。基本思路是把不同的函数形式定义为不同的函数指针,然后以普通函数的形式封装。
虚函数的支持比较复杂,BOOST通过多继承方式支持它,用户需要以多继承定义一个基类的封装,然后暴露这个封装类。在阅读BOOST文档时,要注意封装类在用class_暴露接口时的代码,以下是BOOST文档中的一个示例:
struct BaseWrap : Base, wrapper<Base>
{
    int f()
    {
        if (override f = this->get_override("f"))
            return f(); // *note*
        return Base::f();
    }

    int default_f() { return this->Base::f(); }
};

class_<BaseWrap, boost::noncopyable>("Base")
    .def("f", &Base::f, &BaseWrap::default_f)
    ;注意封装代码中class_的模板参数是BaseWrap,但def中的第二个函数是&Base::f。def的第三个函数提供函数封装的一些信息,这里指明了该虚函数的默认实现。更多的用法请参考Boost文档。在BOOST中还提供了更复杂的功能,比如可以定义Python类的属性(者甚至比在Python中更简单)。 在C++程序中嵌入Python

引用解释器
Python标准的外部调用方法并不复杂,在Python的文档中可以查到。最简单的调用方式如下:

#include <Python.h>

int
main(int argc, char *argv[])
{
  Py_Initialize();
  PyRun_SimpleString("from time import time,ctime/n"
                     "print 'Today is',ctime(time())/n");
  Py_Finalize();
  return 0;
}
Py_Initialize();启动虚拟机,Py_Finalize();终止它。PyRun_SimpleString用于执行语句。Boost延续了这一方式,并着眼于将交互过程变得更简单些。BOOST程序中最简单的示例如下:


Py_Initialize();
object main_module((
    handle<>(borrowed(PyImport_AddModule("__main__")))));

object main_namespace = main_module.attr("__dict__");

handle<> ignored((PyRun_String(

    "hello = file('hello.txt', 'w')/n"
    "hello.write('Hello world!')/n"
    "hello.close()"

  ,Py_file_input
  , main_namespace.ptr()
  , main_namespace.ptr())
));
Py_Finalize();
从这种复杂度的程序中,体现不出BOOST的优势,它甚至比C程序更复杂。不过这里可以学习一下它的基本框架。也许下面这个更为复杂的示例可以说明些问题:

object main_module((
     handle<>(borrowed(PyImport_AddModule("__main__")))));

object main_namespace = main_module.attr("__dict__");

handle<> ignored((PyRun_String(

    "result = 5 ** 2"

    ,Py_file_input
    , main_namespace.ptr()
    , main_namespace.ptr())
));

int five_squared = extract<int>(main_namespace["result"]);我们可以通过这里看到BOOST的一些优势,如通过C++对象(这里main_namespace对象)获取Python数据,并将其转化为C++数据(extract模板)。而下面这个示例演示了BOOST为Python环境提供的C++异常保护:
try
{
    object result((handle<>(PyRun_String(
        "5/0"
      ,Py_eval_input
      , main_namespace.ptr()
      , main_namespace.ptr()))
    ));

    // execution will never get here:
    int five_divided_by_zero = extract<int>(result);
}
catch(error_already_set)
{
    // handle the exception in some way
}当然,我们也可以选择无异常的版本:

handle<> result((allow_null(PyRun_String(
    "5/0"
   ,Py_eval_input
   , main_namespace.ptr()
   , main_namespace.ptr()))));

if (!result)
    // Python exception occurred
else
    // everything went okay, it's safe to use the result
调用脚本


  PyRunString是Python的C-API,Python提供了一系列的调用方法供程序执行脚本使用,也包括了PyRun_File这样的直接调用文件的功能,当然,它不支持C++的I/O,使用的是C风格的文件指针。
  <!--StartFragment -->附记

  最后提一下,使用Boost,无论你是要扩展还是嵌入Python,都需要预编译Boost库,编译方法在Boost的文档中也有介绍。使用Boost的时候,要把预编译好的lib链接到link序列中,还要把DLL放到应用程序可以访问到的地方。

附记
  最后提一下,使用Boost,无论你是要扩展还是嵌入Python,都需要预编译Boost库,编译方法在Boost的文档中也有介绍。使用Boost的时候,要把预编译好的lib链接到link序列中,还要把DLL放到应用程序可以访问到的地方。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-368023-1-1.html 上篇帖子: 初识python模块 下篇帖子: python-mysqldb的安装
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表