BoostQPython是一个开源的C++E序库,提供cMIDL的接口来把C++cd函数l定到Python。借助于C++强大的编译时内省(introspection)能力和最新的元编E?metaprogramming)技术,它完全用C++来实玎ͼ而不用引入新的语法。Boost.Python丰富的特性和高接口使从底层h混合pȝ的方式设计组件成为可能,从而ɽE序员可以轻村֒q诏的同时用C++高效的编译时多态和Python极其方便的运行时多态?/p>
作ؓ两门语言Qpython和C++在很多方面不一栗C++被编译ؓ机器码,python被解?interpreted)执行。Python的动态类?type)pȝl常被认为是灉|性的基础QC++的静态类型是效率的基矟뀂C++有复杂艰q~译时元语言(meta-language)Q而在python里,实际上一切都在运行时发生。然而对很多E序员来_q些不同恰好意味着Python和C++是彼此的完美补。Python E序里的性能瓉部分可以用C++来重写,从而最大化速度。强大的C++E序库的作者选择Python作ؓ中间?middleware)语言Q从而获得灵zȝpȝ集成能力。此外,表面的不同掩盖了二者非常类似的一些地方:
考虑到Python丰富的’C'协作APIQ原则上把C++的类型和函数以类g暴露lC++的接口暴露给Python是可能的。然而,单是Python提供的这U设施对集成C++的支持比较弱。和C++QPython相比Q’C'的抽象机刉常初U,而且完全不支持异常处理。’C'扩展模块的作者必L动管理引用计敎ͼq不但让人恼火的ȝ和单调,q极度容易出错。传l的扩展模块Ҏ产生重复的样板代?boilerplate code)Q从而难于维护,其是要包装的API很复杂时?/p>
上述限制D了一些包装系l的开发。SWIG_ 可能是集成C/C++和Python的包装系l中最行的。一个更q的例子?SIP Q它专门设计来提?Qt 囑Ş用户界面库的Python接口。SWIG和SIP都引入了它们专有的语a来实现语a间绑定。这当然有它的好处,但不得不应付三种不同的语a(PythonQC/C++和接口语a)也带来了实际的和心理上的困难?CXX 软g包展C出它是一个有的包装pȝ。它说明了至一部䆾Python ‘C?API可以通过用户友好得多的C++接口来包装和表现。然而,和SWIG和SIP不一PCXX不支持把C++cd装成新的Pythoncd?/p>
Boost.Python 的特性和目标和很多这Lpȝ有相当程度的重叠。就是说QBoost.Python试图最大化便利性和灉|性,而不引入单独的包装语a。相反,它在q后用静态元~程技术管理很多复杂问题,赋予了用户通过高C++接口来包装C++cd函数的能力,Boost.Python也在如下领域越了早期的pȝQ?/p>
鼓舞Boost.Python开发的关键发现是,传统扩展开发中的大量样板代码都可以通过C++~译时内省来消除。被包装的C++函数的每个参数都必须Ҏ参数cd从Python对象里取出来。类似地Q函数返回值的cd军_了返回值如何从C++转换成Python。参数类型和q回值类型当焉是每个函数的cd的一部分Q正是从q里QBoost.Python推导Z大部分需要的信息?/p>
q种Ҏ导向?用户引导的包?/em> Q尽可能的用UC++的框架直接从要包装的代码里取得信息,q以外的信息qh式提供。大多数引导是自动的Q很需要真正的q涉。因为写接口规范和写被暴露的代码的是同一门全功能语言Q当需要取得控制时用户有了I前强大的能力?/p>
Boost.Python的首要目标是让用户只用C++~译器就能向Python暴露C++cd函数。大体来Ԍ允许用户直接从Python操作C++对象?/p>
然而,有一点很重要Q那是不要 q于 按字面翻译所有接口:必须考虑每种语言的惯用语。例如,虽然C++和Python都有q代器的概念Q表达方式却很不一栗Boost.Python必须能连接这些不同的接口?/p>
必须把Python用户和C++接口的微误用造成的崩溃隔R出于同样原因,应该把C++用户和低UPython ‘C?API隔离Q容易出错的C接口Q比如手动引用计数管理,原始?raw)PyObject指针Q应该用更加健壮?more-robust)替代物来取代?/p>
支持Zlg的开发是臛_重要的,因此被暴露在一个扩展模块里的C++cd应该能够被传递给被暴露在另一个模块中的函敎ͼ而不丢失重要的信息,比如说C++l承关系?/p>
最后,所有的包装必须?非R入的(non-intrusive) Q不能修改甚至看不到原始的C++代码。对只能看见它头文g和二q制文g的第三方Q现有的C++库必L可包装的?/p>
现在来预览一下Boost.PythonQƈ看看它如何改qPython的原始包装功能。下面是我们x露的一个函?
char const* greet(unsigned x) { static char const* const msgs[] = { "hello"Q?Boost.Python"Q?world!" }; if (x > 2) throw std::range_error("greet: index out of range"); return msgs[x]; }
用Python的C API和标准C++来包装这个函敎ͼ我们需要像q样:
extern "C" // 所有Python交互都用C链接和调用习? { // 处理参数/l果转换和检查的包装? PyObject* greet_wrap(PyObject* argsQPyObject * keywords) { int x; if (PyArg_ParseTuple(argsQ?i"Q?amp;x)) // 取出/查参? { char const* result = greet(x); // 调用被包装的函数 return PyString_FromString(result); // l果转换成Python } return 0; // 发生了错? } // 待包装函数表Q函数用q个模块来暴? static PyMethodDef methods[] = { { "greet"Qgreet_wrapQMETH_VARARGSQ?return one of 3 parts of a greeting" } Q{ NULLQNULLQ?QNULL } // sentinel }; // 模块初始化函? DL_EXPORT init_hello() { (void) Py_InitModule("hello"Qmethods); // d成员函数(method)到模? } }
现在看看我们使用Boost.Python来暴露它时的包装代码:
#include <boost/python.hpp> using namespace boost::python; BOOST_PYTHON_MODULE(hello) { def("greet"QgreetQ?return one of 3 parts of a greeting"); }
下面是用它的代?
>>> import hello >>> for x in range(3): ... print hello.greet(x) ... hello Boost.Python world!
C API版本要冗长的多,此外Q一些它没有正确处理的地方值得提到Q?/p>
q部分简要描qC库的主要Ҏ。ؓ了避免淆,忽略了实现细节?/p>
C++cdl构以类似的z的接口来暴霌Ӏ假设有:
struct World { void set(std::string msg) { this->msg = msg; } std::string greet() { return msg; } std::string msg; };
下面的代码将在我们的扩展模块里暴露它:
#include <boost/python.hpp> BOOST_PYTHON_MODULE(hello) { class_<World>("World") .def("greet"Q?amp;World::greet) .def("set"Q?amp;World::set) ; }
管上述代码有某U熟悉的Pythonic的感觉,但语法有时还是有点o惑,因ؓ它看h不像Z习惯的C++代码。但是,q仍然只是标准C++。因为它们灵zȝ语法和操作符重蝲QC++和Python都很适于定义特定领域(?语言( domain-specific (sub)languages) (DSLs)。那是我们在Boost.Python里所做的。把代码拆开来看:
class_<World>("World")
构造类型class_<World>的未命名对象Q把"World"传给它的构造器。这在扩展模块里创Z个叫World的new-style Pythonc,q把它和C++cdWorld在Boost.Python的类型{换注册表里关联v来。我们也可以q么?
class_<World> w("World");
但那样做的话会更J琐Q因为我们不得不再次命名w以调用它的def()成员函数:
w.def("greet"Q?amp;World::greet)
原来的例子里表示成员q入的点的位|没有什么特别的QC++允许L的空白符出现在表意符?token)的Q一边,把点攑֜每行的开始允许用l一的语法把q箋的调用都串v来,不管我们想串多少都行。另一个允许的串接的事实是class_<>成员函数都返回对*this的引用?/p>
因此原来的例子等同于:
class_<World> w("World"); w.def("greet"Q?amp;World::greet); w.def("set"Q?amp;World::set);
能这h分Boost.Pythoncd装层的组成部分有时候是有用的,但本文的剩下部分一直用简z的语法?/p>
最后来看包装类被用的情况:
>>> import hello >>> planet = hello.World() >>> planet.set('howdy') >>> planet.greet() 'howdy'
因ؓ我们的Worldcd是一个简单的structQ它有一个隐式的无参?no-argument)(nullary)构造器。Boost.Python默认暴露nullary构造器Q这是我们可以像下面这样写的原?
>>> planet = hello.World()
然而不哪门语aQ设计得好的cd能都需要构造器参数Q以建立他们的不变量(invariants)。在Python里,__init__只是一个特D名U的成员函数(method)Q与q不同,C++里的构造器不能像普通成员函数那样处理。特别是我们不能取它的地址: &World::Worldq样会被报错。库提供了一个不同的接口来指定构造器。假设有:
struct World { World(std::string msg); // d的构造器 ...
我们可以q样修改包装代码:
class_<World>("World"Qinit<std::string>()) .def(init<doubleQdouble>()) ...
当然QC++cd能还有其他的构造器Q我们也可以暴露他们Q只需要向def()传递更多init<?gt;的实?
class <World>("World"Qinit<std::string>()) .def(init<doubleQdouble>()) ...
Boost.Python允许被包装的函数Q成员函C及构造器被重载,以映C++重蝲?/p>
C++中的M可公q讉K的数据成员都能轻易的被包装成只读或者只写属?attributes):
class_<World>("World"Qinit<std::string>()) .def_readonly("msg"Q?amp;World::msg) ...
q直接在Python里?
>>> planet = hello.World('howdy') >>> planet.msg 'howdy'
q不会导致添加属性到World实例__dict__Q从而在包装大型数据l构时节省大量的内存。实际上Q除非从Python昑ּd属性,否则实例__dict__Ҏ不会被创建。Python的这U能力来源于新的Python 2.2 cdpȝQ尤其是descriptor接口和propertycd?/p>
在C++里,可公q讉K的数据成员被认ؓ是糟p设计的表现Q因Z们破坏了装(encapsulation)Q文体向?style guides)通常指示代之?getter" ?"setter"函数。然而在Python里,__getattr__Q__setattr__和从2.2开始有的property意味着属性进入只是程序员控制下的装得更好的语法工具。通过让Python property对用L接可用,Boost.Pythonq接了二者的不同惯用语。如果msg是私有的Q我们仍然能把它暴露为Python里的属?
class_<World>("World"Qinit<std::string>()) .add_property("msg"Q?amp;World::greetQ?amp;World::set) ...
上面的例子映了Z熟悉的Python 2.2+里的property用法:
>>> class World(object): ... __init__(selfQmsg): ... self.__msg = msg ... def greet(self): ... return self.__msg ... def set(selfQmsg): ... self.__msg = msg ... msg = property(greetQset)
能给用户定义cd定义术操作W一直是两门语言的数D取得成功一个重要因素。像 Numpy q样的Y件包的成功证明了在扩展模块中暴露操作W能产生巨大能量。Boost.Pythonl包装操作符重蝲提供了简z的机制。下面是包装Boost的有理数? rational number library)的代码的片断:
class_<rational<int> >("rational_int") .def(init<intQint>()) // constructorQe.g. rational_int(3Q?) .def("numerator"Q?amp;rational<int>::numerator) .def("denominator"Q?amp;rational<int>::denominator) .def(-self) // __neg__ (unary minus) .def(self + self) // __add__ (homogeneous) .def(self * self) // __mul__ .def(self + int()) // __add__ (heterogenous) .def(int() + self) // __radd__
q种法是通过单的应用"表达式模?("expression templates") [VELD1995] 来施加的Q?表达式模?是一U最初ؓ优化高性能矩阵代数表达式而开发的技术。本质是不立卌行计,而重载操作符来构造描q计的cd。在矩阵代数里,当考虑整个表达式的l构Q而不?贪婪?Ҏ步操作求值时Q经常可以获得戏剧性的优化。Boost.Python用同L技术来构徏Z包含 self 的表辑ּ的适当的Python 成员函数对象(method object)?/p>
要在Boost.Python里描qC++l承关系Q可以像下面q样把可选的bases<?gt;参数d到class_<?gt;模板参数表里:
class_<DerivedQbases<Base1QBase2> >("Derived") ...
q样有两个效果:
当然从被包装的C++cd例衍生新的PythoncL可能的。这是因为Boost.Python使用了new-style classpȝQ这套系l在Python内置cd上工作良好。但有一个重要细节不同: Python内置cd一般在__new__函数里徏立不变量(invariants)Q从而衍生类不用在调用它的成员函数前调用基类的__init__:
>>> class L(list): ... def __init__(self): ... pass ... >>> L().reverse() >>>
因ؓC++对象构造是一步操?one-step operation)Q直到参数可用C++实例数据才能被构造,在__init__函数?
>>> class D(SomeBoostPythonClass): ... def __init__(self): ... pass ... >>> D().some_boost_python_method() Traceback (most recent call last): File "<stdin>"Qline 1Qin ? TypeError: bad argument type for built-in operation
发生错误的原因是Boost.Python 在实例D里找不到cdSomeBoostPythonClass的实例数据;D的__init__函数遮盖了基cȝ构造函数。可以通过删除D的__init__函数或是让它昑ּ的调用SomeBoostPythonClass.__init__(?来纠正错误?/p>
在Python里从扩展c衍生新的类型没太大意思,除非它们在C++里能被多态的使用。换句话_当在C++里通过基类指针/引用调用Python成员函数ӞPython成员函数的实现应该看h像是覆盖(override)了C++虚函数的实现。因改变虚函数的行ؓ的唯一Ҏ是在衍生c里覆盖(override)它,用户必须构造一个特D的衍生cL分派(dispatch)多态类的虚函数?
// // 要包装的接口: // class Base { public: virtual int f(std::string x) { return 42; } virtual ~Base(); }; int calls_f(Base const& bQstd::string x) { return b.f(x); } // // 包装代码 // // 分派者类(Dispatcher class) struct BaseWrap : Base { // 储存指向Python对象的指? BaseWrap(PyObject* self_) : self(self_) {} PyObject* self; // 当f没有被覆?override)时的~省实现 int f_default(std::string x) { return this->Base::f(x); } // 分派实现 int f(std::string x) { return call_method<int>(selfQ?f"Qx); } }; ... def("calls_f"Qcalls_f); class_<BaseQBaseWrap>("Base") .def("f"Q?amp;Base::fQ?amp;BaseWrap::f_default) ;
下面是一些python演示代码:
>>> class Derived(Base): ... def f(selfQs): ... return len(s) ... >>> calls_f(Base()Q?foo') 42 >>> calls_f(Derived()Q?forty-two') 9
对分z者类(Dispatcher class)Q要注意:
无可否认Q重复这U公式化的流E是冗长乏味的。尤其是目里有大量多态类的时候。这反映了C++~译时内省能力的必然限制:无法枚Dcȝ成员来判断哪个是虚函数。不q,一个很有希望的目已经启动Q致力于写一个前端程序来从C++头文件自动生成这些分z者类(以及其它包装代码)?/p>
Pyste 是由Bruno da Silva de Oliveira开发的Q基?GCC_XML 构徏。GCC_XML可以生成XML版本的GCC内部E序描述。GCC是一U高度符?译注QC++标准)的编译器Q从而确保了Ҏ复杂的模板代码的正确处理和对底层cdpȝ的完全访问。和Boost.Python的哲学一_Pyste接口描述既不侵入被包装的代码Q也不用某种不熟悉的语言来表达,相反Q它?00%的纯Python脚本。如果Pyste成功的话Q将标志着我们的很多用户不用再什么都直接用C++包装。它允许我们选择把一些元E序(metaprogram)代码从C++Ud到Python。我们期待不久后不仅用户QBoost.Python开发者自׃能以混合的思\来考虑("thinking hybrid")他们自己的代码?/p>
序列化是把内存中的对象{换成可以保存到磁盘上或通过|络传送的格式的过E。序列化后的对象(最常见的是单字W串)可以被重新取得ƈ转换回原来的对象。好的序列化pȝ能够自动转换整个对象层次(object hierarchies)。Python的标准pickle模块正是q样的系l。它利用语言强大的运行时内省来序列化几乎是Q意的用户定义对象。加上一些简单的非R入限定,q种强大的设施可以被扩展成对被包装的C++对象也有效。下面是一个例?
#include <string> struct World { World(std::string a_msg) : msg(a_msg) {} std::string greet() const { return msg; } std::string msg; }; #include <boost/python.hpp> using namespace boost::python; struct World_picklers : pickle_suite { static tuple getinitargs(World const& w) { return make_tuple(w.greet()); } }; BOOST_PYTHON_MODULE(hello) { class_<World>("World"Qinit<std::string>()) .def("greet"Q?amp;World::greet) .def_pickle(World_picklers()) ; }
现在让我们创Z个World对象q把它放到磁盘上:
>>> import hello >>> import pickle >>> a_world = hello.World("howdy") >>> pickle.dump(a_worldQopen("my_world"Q?w"))
在可能不同的计算机的可能不同的操作系l的可能不同的脚本中:
>>> import pickle >>> resurrected_world = pickle.load(open("my_world"Q?r")) >>> resurrected_world.greet() 'howdy'
当然也可以用cPickle来获得更快的处理速度?/p>
Boost.Python的pickle_suite完全支持标准Python文定义的pickle协议。类似Python里的__getinitargs__函数Qpickle_suite的getinitargs()负责创徏参数tuple来重pickle了的对象。Python pickle协议中的其他元素Q__getstate__ 和__setstate__可以通过C++ getstate和setstate函数来可选的提供。C++的静态类型系l允许库在编译时保没有意义的函数组?例如Q没有setstate getstate)不会被用?/p>
要想序列化更复杂的C++对象需要做比上面的例子E微多点的工作。幸q的是Object接口(参见下一?在保持代码便于管理上帮了大忙?/p>
无所不在的PyObject*Q手动引用计敎ͼ需要记住是哪个API调用q回?新的"(自n拥有?引用或是"借来?(原始?引用Q这些可能有l验的C语言扩展模块的作者都熟悉。这些约束不仅麻烦,更是错误的主要来源,其是存在异常的时候?/p>
Boost.Python提供了一个objectc,它自动化了引用计数ƈ提供Lcd的C++对象到Python的{换。这极大的减M未来的扩展模块作者的学习负担?/p>
从Q一cd创徏object极度?
object s("helloQworld"); // s 理一个Python字符?
object可以和所有其它类型进行模板化的交互,自动的进行到Python的{换。这一切自然得很容易被忽略不计:
object ten_Os = 10 * s[4]; // -> "oooooooooo"
上面的例子中Q在索引和乘法操作被调用前,4?0被{换成了Python对象?/p>
extract<T>cL板可以用来把Python对对象{换成C++cd:
double x = extract<double>(o);
如果M方向的{换不能执行,在q行时抛Z个适当的异常?/p>
伴随objectcd的是一套衍生类型,可能的映射Python的内|类?listQdictQtuple{?。这样就能方便的从C++操作q些高cd?
dict d; d["some"] = "thing"; d["lucky_number"] = 13; list l = d.keys();
q看h和工作v来几乎就像是通常的python代码Q但它实际上是纯的C++。当然我们能包装接受或返回object实例的函数?/p>
因ؓl合~程语言h实际的和心理的困难,在进行Q何实际开发前军_使用单一的语a是普遍现象。对很多应用来说Q性能上的考虑军_了核心算法要用编译语a实现。不q的是,因ؓ静态类型系l的复杂性,我们行时性能要付出开发时间大量增长的代h。经验表明,和开发相应的Python代码比v来,开发可l护的C++代码通常需要更长的旉和艰隑־多才能获得的工作l验。即使开发者觉得只用一门编译语a开发挺好,Z用户Q他们也l常用某U类型的特别的脚本层来补充系l,哪怕他们永q不会得到同L好处?/p>
Boost.Python让我们能 think hybrid 。Python可以用来快速搭建新的应用的原型Q在开发能工作的系l时Q它的易用性和大量的标准库使我们处于领先。需要的话,可以用能工作的代码来扑և限制速度的热?rate-limiting hotspots)。ؓ了最大化性能Q这些热点可以用C++来重新实玎ͼ然后用Boost.Pythonl定来连q已有的高q程(译注Q指PythonE序)?/p>
当然Q如果一开始就清楚很多法最后不得不用C++来实玎ͼq种 由顶至下(top-down) 的方法就没那么吸引h了。幸q的是,Boost.Python也允许我们?由底至上(bottom-up) 的方法。我们非常成功的把这U方法用在了一个用于科学应用的工具pY件的开发上。这个工L开始主要是一个带Boost.Pythonl定的C++cdQ接下来有一段旉增长主要集中在C++部分Q随着工具变得越来越完整Q越来越多新d的功能可以用Python来实现?/p>
上图是实现新的算法时新添加的C++代码和Python代码的估计比率随旉变化的情c我们预计这个比率会辑ֈ接近70% (Python)。能够主要用Python而不是更困难的静态类型语a来解x问题Q这是我们在Boost.Python上的投入的回报。我们的所有代码都能从Python讉KQ这使得更广泛的开发者可以用它来快速开发新的应用?/p>
Boost.Python的第一版是由Dave Abrahams在Dragon Systems开发的。在那里他非常荣q的请到Tim Peters作ؓ他的"Python之禅"( "The Zen of Python")导师。Dave的工作之一是开发基于Python的自然语a处理pȝ。因为最l要被用于嵌入式gQ系l计密集的内核L被假设成要用C++来重写以优化速度和内存需求量(memory footprint) [1] 。这个项目也想用Python试脚本 [2] 来测试所有的C++代码。当时我们知道的l定C++和Python的唯一工具?SWIG Q但那时它处理C++的能力比较弱。要说在那时对Boost.Python所使用Ҏ的可能优之处有了什么深L见,那是骗h的。Dave对花俏的C++模板技巧的兴趣和娴熟刚好到了能真正做点什么的时候,Boost.Python那样出CQ因为它满了需求,因ؓ它看h挺酷Q值得一试?/p>
早期的版本针对的许多基本目标和在q篇论文中描q的相同。最显著的区别在于早期版本的语法要稍微麻烦一点,~ZҎ作符重蝲QpicklingQ基于组件的开发的专门支持。后面三个特性很快就被Ullrich Koethe和Ralf Grosse-Kunstleve [3] 加上了。其他热心的贡献?contributors)也出来A献了一些改q,如对嵌套模块和成员函数的支持{?/p>
?001q早期时开发已l稳定下来了Q很有新的Ҏ添加,然而这时一个烦人的问题暴露出来了:Ralf已经开始在一个?EDG 前端的编译器的预发布版上试Boost.PythonQ这时Boost.Python内核中负责处理Python和C++的类型{换的机制(mechanism)~译p|了。结果证明我们一直在利用一个bugQ这个bug在所有我们测试过的C++~译器实C都非常普遍。我们知道随着C++~译器很快变得更加标准兼容,库将开始在更多的^Cp|。很不幸Q因套机制是库的功能的中枢,解决问题看v来非常困难?/p>
q运的是那一q的后期QLawrence Berkeley和后来的Lawrence Livermore National labs?Boost Consulting {订了支持Boost.Python的开发的合同。这样就有了新的Z来处理库的基本问题,从而确保将来的发展。重新设计开始于低cd转换架构Q内|的标准兼容和对Zlg的开发的支持(和不得不昑ּ的跨模块边界导入或导出转换的第一版Ş成对?。对Python和C++对象的关p进行了分析Q从而能更直观的处理C++ lvalues和rvalues?/p>
Python 2.2里出现的强大的新cdpȝ使得选择是否l护对Python 1.5.2的兼Ҏ变得容易了Q这个丢弃大量精心制作的仅仅用来模拟classic Pythoncȝ代码的机会,好的令h无法拒绝。另外,Python iterators ?descriptors提供了重要且优雅的工h描述cM的C++l构。一般化了的对象接口的开发允许我们进一步把C++E序员和使用Python C API带来的危险性和语法负担隔离开。大量的其他Ҏ在q个阶段被加了进来,包括C++异常译Q改q的重蝲函数支持Q还有最重要的用来处理指针和引用的CallPolicies?/p>
?002q十月,W二版的Boost.Python发布了。那以后的开发集中在改进对C++q行时多态和指针的支持上。特别是Peter Dimov的y妙的boost::shared_ptr设计使我们能l和系l开发者提供一致的接口Q用于跨语a藩篱来回Ud对象而不丢失信息?/p>
刚开始,我们担心Boost.Python v2的复杂性会ȝ贡献者,?Pyste 和几个其他重要特性的贡献(contribution)的出Cɘq些担心昑־多余了。每天出现在Python C++-sig上的问题和希望得到的改进的积?backlog)表明了库正在被用。对我们来说Q未来看h很光明?/p>
Boost.Python 实现了两U功能丰富的优秀的语a环境间的无缝协作。因为它利用模板元编E技术来对类型和函数q行内省Q用hq用不着再学W三U语a:接口定义是用z而可l护的C++写的。同P包装pȝ不用再解析C++头文件或是描q类型系l:~译器都l我们做了?/p>
计算密集的Q务适合强大的C++Q它一般不可能用纯Python来实现。然而像序列化这L工作Q可能用Python很简单,用C++非常困难。假如有从底层开始构建合系l的奢侈Q我们有新的信心和动力来q行设计?/p>