Boost.Python教程:通用技术
下面介紹一些有用的技術(shù),您可以在使用Boost.Python包裝代碼時(shí)使用這些技術(shù)。
創(chuàng)建包
Python包是一組模塊,為用戶提供某種功能。?如果您不熟悉如何創(chuàng)建包,?Python教程中提供了對(duì)它們的一個(gè)很好的介紹。
但是我們使用Boost.Python包裝C ++代碼。?我們?nèi)绾螢橛脩籼峁┮粋€(gè)漂亮的包界面??為了更好地解釋一些概念,讓我們使用一個(gè)例子。
我們有一個(gè)有不同含義的C ++庫(kù):讀取和寫入各種格式,對(duì)聲音數(shù)據(jù)應(yīng)用濾波器等。它被命名(方便)?sounds?。?我們的庫(kù)已經(jīng)有了一個(gè)簡(jiǎn)潔的C ++命名空間層次結(jié)構(gòu),如下所示:
sounds::core sounds::io sounds::filters我們想向Python用戶提供相同的層次結(jié)構(gòu),允許他編寫如下代碼:
import sounds.filters sounds.filters.echo(...) # echo is a C++ function第一步是編寫包裝代碼。?我們必須使用Boost.Python單獨(dú)導(dǎo)出每個(gè)模塊,如下所示:
/* file core.cpp */ BOOST_PYTHON_MODULE(core) {/* export everything in the sounds::core namespace */... }/* file io.cpp */ BOOST_PYTHON_MODULE(io) {/* export everything in the sounds::io namespace */... }/* file filters.cpp */ BOOST_PYTHON_MODULE(filters) {/* export everything in the sounds::filters namespace */... }編譯這些文件將生成以下Python擴(kuò)展:?core.pyd?,?io.pyd和io.pyd?。
| 注意 |
| 擴(kuò)展名.pyd用于python擴(kuò)展模塊,它們只是共享庫(kù)。?使用系統(tǒng)的默認(rèn)設(shè)置,例如Unix的.so和Windows的.dll?,也可以。 |
現(xiàn)在,我們?yōu)镻ython包創(chuàng)建這個(gè)目錄結(jié)構(gòu):
sounds/__init__.pycore.pydfilters.pydio.pyd文件__init__.py告訴Python該目錄sounds/實(shí)際上是一個(gè)Python包。?它可以是一個(gè)空文件,但也可以執(zhí)行一些魔術(shù),稍后將會(huì)顯示。
現(xiàn)在我們的包準(zhǔn)備好了。?用戶所要做的就是將sounds放入他的PYTHONPATH并啟動(dòng)解釋器:
>>> import sounds.io >>> import sounds.filters >>> sound = sounds.io.open('file.mp3') >>> new_sound = sounds.filters.echo(sound, 1.0)很不錯(cuò)吧?
這是創(chuàng)建包層次結(jié)構(gòu)的最簡(jiǎn)單方法,但它不是很靈活。?如果我們想要將純?Python函數(shù)添加到過(guò)濾器包中,例如,一次在聲音對(duì)象中應(yīng)用3個(gè)過(guò)濾器,該怎么辦??當(dāng)然,您可以在C ++中執(zhí)行此操作并將其導(dǎo)出,但為什么不在Python中執(zhí)行此操作??您不必重新編譯擴(kuò)展模塊,而且編寫它會(huì)更容易。
如果我們想要這種靈活性,我們將不得不使我們的包層次結(jié)構(gòu)復(fù)雜化。?首先,我們將不得不更改擴(kuò)展模塊的名稱:
/* file core.cpp */ BOOST_PYTHON_MODULE(_core) {.../* export everything in the sounds::core namespace */ }請(qǐng)注意,我們?cè)谀K名稱中添加了下劃線。?文件名也必須更改為_(kāi)core.pyd?,我們對(duì)其他擴(kuò)展模塊執(zhí)行相同的操作。?現(xiàn)在,我們改變我們的包層次結(jié)構(gòu)如下:
sounds/__init__.pycore/__init__.pycore.pydfilters/\_init__.pyfilters.pydio/\_init__.py_io.pyd請(qǐng)注意,我們?yōu)槊總€(gè)擴(kuò)展模塊創(chuàng)建了一個(gè)目錄,并為每個(gè)擴(kuò)展模塊添加了__init__.py。?但是如果我們這樣離開(kāi),用戶將不得不使用以下語(yǔ)法訪問(wèn)核心模塊中的函數(shù):
>>> import sounds.core._core >>> sounds.core._core.foo(...)這不是我們想要的。?但是這里輸入__init__.py魔術(shù):用戶可以直接訪問(wèn)帶到__init__.py命名空間的所有內(nèi)容。?因此,我們所要做的就是將整個(gè)命名空間從_core.pyd帶到core/__init__.py?。?所以將這行代碼添加到sounds/core/__init__.py?:
from _core import *我們對(duì)其他包也這樣做。?現(xiàn)在,用戶像以前一樣訪問(wèn)擴(kuò)展模塊中的函數(shù)和類:
>>> import sounds.filters >>> sounds.filters.echo(...)另外一個(gè)好處是我們可以輕松地將純Python函數(shù)添加到任何模塊中,這樣用戶就無(wú)法區(qū)分C ++函數(shù)和Python函數(shù)。?讓我們將一個(gè)純?Python函數(shù)echo_noise添加到filters包中。?此函數(shù)在給定的sound對(duì)象中按順序應(yīng)用echo和noise濾波器。?我們創(chuàng)建一個(gè)名為sounds/filters/echo_noise.py的文件并編寫我們的函數(shù):
import _filters def echo_noise(sound):s = _filters.echo(sound)s = _filters.noise(sound)return s接下來(lái),我們將此行添加到sounds/filters/__init__.py?:
from echo_noise import echo_noise就是這樣。?用戶現(xiàn)在可以像filters包中的任何其他功能一樣訪問(wèn)此功能:
>>> import sounds.filters >>> sounds.filters.echo_noise(...)在Python中擴(kuò)展包裝對(duì)象
由于Python的靈活性,您可以輕松地向類添加新方法,即使它已經(jīng)創(chuàng)建了:
>>> class C(object): pass >>> >>> # a regular function >>> def C_str(self): return 'A C instance!' >>> >>> # now we turn it in a member function >>> C.__str__ = C_str >>> >>> c = C() >>> print c A C instance! >>> C_str(c) A C instance!是的,Python rox。?
我們可以對(duì)使用Boost.Python包裝的類執(zhí)行相同的操作。?假設(shè)我們?cè)贑 ++中有一個(gè)類point?:
class point {...};BOOST_PYTHON_MODULE(_geom) {class_<point>("point")...; }如果我們使用上一個(gè)會(huì)話創(chuàng)建包中的技術(shù)?,我們可以直接編碼到geom/__init__.py?:
from _geom import *# a regular function def point_str(self):return str((self.x, self.y))# now we turn it into a member function point.__str__ = point_str從C ++創(chuàng)建的所有點(diǎn)實(shí)例也將具有此成員函數(shù)!?這種技術(shù)有幾個(gè)優(yōu)點(diǎn):
- 對(duì)于這些附加功能,將編譯時(shí)間減少到零
- 將內(nèi)存占用減少到幾乎為零
- 最大限度地減少重新編譯的需要
- 快速原型設(shè)計(jì)(如果需要,您可以將代碼移動(dòng)到C ++而無(wú)需更改接口)
您甚至可以使用元類添加一點(diǎn)語(yǔ)法糖。?讓我們創(chuàng)建一個(gè)特殊的元類,在其他類中“注入”方法。
# The one Boost.Python uses for all wrapped classes. # You can use here any class exported by Boost instead of "point" BoostPythonMetaclass = point.__class__class injector(object):class __metaclass__(BoostPythonMetaclass):def __init__(self, name, bases, dict):for b in bases:if type(b) not in (self, type):for k,v in dict.items():setattr(b,k,v)return type.__init__(self, name, bases, dict)# inject some methods in the point foo class more_point(injector, point):def __repr__(self):return 'Point(x=%s, y=%s)' % (self.x, self.y)def foo(self):print 'foo!'現(xiàn)在讓我們看看它是如何得到的:
>>> print point() Point(x=10, y=10) >>> point().foo() foo!另一個(gè)有用的想法是用工廠函數(shù)替換構(gòu)造函數(shù):
_point = pointdef point(x=0, y=0):return _point(x, y)在這個(gè)簡(jiǎn)單的情況下,沒(méi)有太多的收獲,但對(duì)于具有許多重載和/或參數(shù)的構(gòu)造函數(shù),這通常是一個(gè)很大的簡(jiǎn)化,同樣幾乎沒(méi)有內(nèi)存占用和關(guān)鍵字支持的零編譯時(shí)開(kāi)銷。
縮短編譯時(shí)間
如果您曾經(jīng)導(dǎo)出過(guò)很多類,那么您知道編譯Boost.Python包裝器需要相當(dāng)長(zhǎng)的時(shí)間。?此外,內(nèi)存消耗很容易變得太高。?如果這導(dǎo)致您出現(xiàn)問(wèn)題,可以將class_ definitions拆分為多個(gè)文件:
/* file point.cpp */ #include <point.h> #include <boost/python.hpp>void export_point() {class_<point>("point")...; }/* file triangle.cpp */ #include <triangle.h> #include <boost/python.hpp>void export_triangle() {class_<triangle>("triangle")...; }現(xiàn)在,您創(chuàng)建一個(gè)main.cpp文件,其中包含BOOST_PYTHON_MODULE宏,并調(diào)用其中的各種導(dǎo)出函數(shù)。
void export_point(); void export_triangle();BOOST_PYTHON_MODULE(_geom) {export_point();export_triangle(); }編譯并鏈接所有這些文件會(huì)產(chǎn)生與通常方法相同的結(jié)果:
#include <boost/python.hpp> #include <point.h> #include <triangle.h>BOOST_PYTHON_MODULE(_geom) {class_<point>("point")...;class_<triangle>("triangle")...; }但記憶得到了控制。
如果您正在開(kāi)發(fā)C ++庫(kù)并同時(shí)將其導(dǎo)出到Python,也建議使用此方法:類中的更改只需要編譯單個(gè)cpp,而不是整個(gè)包裝器代碼。
| 注意 |
| 如果在編譯大型源文件時(shí)收到錯(cuò)誤消息“致命錯(cuò)誤C1204:編譯器限制:內(nèi)部結(jié)構(gòu)溢出”?,此方法也很有用,如FAQ中所述。 |
總結(jié)
以上是生活随笔為你收集整理的Boost.Python教程:通用技术的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2019热门创业赛事活动发布平台
- 下一篇: 使用 Saleae LLC 监控 781