擴(kuò)展/嵌入常見問題?

可以使用 C 語(yǔ)言創(chuàng)建自己的函數(shù)嗎??

是的,您可以在C中創(chuàng)建包含函數(shù)、變量、異常甚至新類型的內(nèi)置模塊。在文檔 擴(kuò)展和嵌入 Python 解釋器 中有說明。

大多數(shù)中級(jí)或高級(jí)的Python書籍也涵蓋這個(gè)主題。

可以使用 C++ 語(yǔ)言創(chuàng)建自己的函數(shù)嗎??

是的,可以使用C ++中兼容C的功能。 在Python include文件周圍放置` extern“C”{...}` ,并在Python解釋器調(diào)用的每個(gè)函數(shù)之前放置 extern“C” 。 具有構(gòu)造函數(shù)的全局或靜態(tài)C ++對(duì)象可能不是一個(gè)好主意。

C很難寫,有沒有其他選擇??

編寫自己的C擴(kuò)展有很多選擇,具體取決于您要做的事情。

Cython 及其相關(guān)的 Pyrex 是接受稍微修改過的Python形式并生成相應(yīng)C代碼的編譯器。 Cython和Pyrex可以編寫擴(kuò)展而無(wú)需學(xué)習(xí)Python的C API。

如果需要連接到某些當(dāng)前不存在Python擴(kuò)展的C或C ++庫(kù),可以嘗試使用 SWIG 等工具包裝庫(kù)的數(shù)據(jù)類型和函數(shù)。 SIP , CXX Boost , 或 Weave 也是包裝C ++庫(kù)的替代方案。

如何在 C 中執(zhí)行任意 Python 語(yǔ)句??

執(zhí)行此操作的最高層級(jí)函數(shù)為 PyRun_SimpleString(),它接受單個(gè)字符串參數(shù)用于在模塊 __main__ 的上下文中執(zhí)行并在成功時(shí)返回 0 而在發(fā)生異常 (包括 SyntaxError) 時(shí)返回 -1。 如果你想要更多可控性,可以使用 PyRun_String();請(qǐng)?jiān)?Python/pythonrun.c 中查看 PyRun_SimpleString() 的源碼。

如何在 C 中對(duì)任意 Python 表達(dá)式求值??

可以調(diào)用前一問題中介紹的函數(shù) PyRun_String() 并附帶起始標(biāo)記符 Py_eval_input;它會(huì)解析表達(dá)式,對(duì)其求值并返回結(jié)果值。

如何從Python對(duì)象中提取C的值??

這取決于對(duì)象的類型。 如果是元組,PyTuple_Size() 可返回其長(zhǎng)度而 PyTuple_GetItem() 可返回指定序號(hào)上的項(xiàng)。 對(duì)于列表也有類似的函數(shù) PyListSize()PyList_GetItem()。

對(duì)于字節(jié)串,PyBytes_Size() 可返回其長(zhǎng)度而 PyBytes_AsStringAndSize() 提供一個(gè)指向其值和長(zhǎng)度的指針。 請(qǐng)注意 Python 字節(jié)串可能為空,因此 C 的 strlen() 不應(yīng)被使用。

要檢測(cè)一個(gè)對(duì)象的類型,首先要確保它不為 NULL,然后使用 PyBytes_Check(), PyTuple_Check(), PyList_Check() 等等。

還有一個(gè)針對(duì) Python 對(duì)象的高層級(jí) API,通過所謂的‘抽象’接口提供 —— 請(qǐng)參閱 Include/abstract.h 了解詳情。 它允許使用 PySequence_Length(), PySequence_GetItem() 這樣的調(diào)用來(lái)與任意種類的 Python 序列進(jìn)行對(duì)接,此外還可使用許多其他有用的協(xié)議例如數(shù)字 (PyNumber_Index() 等) 以及 PyMapping API 中的各種映射等等。

如何使用Py_BuildValue()創(chuàng)建任意長(zhǎng)度的元組??

不可以。應(yīng)該使用 PyTuple_Pack() 。

如何從C調(diào)用對(duì)象的方法??

可以使用 PyObject_CallMethod() 函數(shù)來(lái)調(diào)用某個(gè)對(duì)象的任意方法。 形參為該對(duì)象、要調(diào)用的方法名、類似 Py_BuildValue() 所用的格式字符串以及要傳給方法的參數(shù)值:

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

這適用于任何具有方法的對(duì)象 —— 不論是內(nèi)置方法還是用戶自定義方法。 你需要負(fù)責(zé)對(duì)返回值進(jìn)行最終的 Py_DECREF() 處理。

例如調(diào)用某個(gè)文件對(duì)象的 "seek" 方法并傳入?yún)?shù) 10, 0 (假定文件對(duì)象的指針為 "f"):

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

請(qǐng)注意由于 PyObject_CallObject() 總是 接受一個(gè)元組作為參數(shù)列表,要調(diào)用不帶參數(shù)的函數(shù),則傳入格式為 "()",要調(diào)用只帶一個(gè)參數(shù)的函數(shù),則應(yīng)將參數(shù)包含于圓括號(hào)中,例如 "(i)"。

如何捕獲PyErr_Print()(或打印到stdout / stderr的任何內(nèi)容)的輸出??

在 Python 代碼中,定義一個(gè)支持 write() 方法的對(duì)象。 將此對(duì)象賦值給 sys.stdoutsys.stderr。 調(diào)用 print_error 或者只是允許標(biāo)準(zhǔn)回溯機(jī)制生效。 在此之后,輸出將轉(zhuǎn)往你的 write() 方法所指向的任何地方。

做到這一點(diǎn)的最簡(jiǎn)單方式是使用 io.StringIO 類:

>>>
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

實(shí)現(xiàn)同樣效果的自定義對(duì)象看起來(lái)是這樣的:

>>>
>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!

如何從C訪問用Python編寫的模塊??

你可以通過如下方式獲得一個(gè)指向模塊對(duì)象的指針:

module = PyImport_ImportModule("<modulename>");

如果模塊尚未被導(dǎo)入(即它還不存在于 sys.modules 中),這會(huì)初始化該模塊;否則它只是簡(jiǎn)單地返回 sys.modules["<modulename>"] 的值。 請(qǐng)注意它并不會(huì)將模塊加入任何命名空間 —— 它只是確保模塊被初始化并存在于 sys.modules 中。

之后你就可以通過如下方式來(lái)訪問模塊的屬性(即模塊中定義的任何名稱):

attr = PyObject_GetAttrString(module, "<attrname>");

調(diào)用 PyObject_SetAttrString() 為模塊中的變量賦值也是可以的。

如何在 Python 中對(duì)接 C ++ 對(duì)象??

根據(jù)你的需求,可以選擇許多方式。 手動(dòng)的實(shí)現(xiàn)方式請(qǐng)查閱 "擴(kuò)展與嵌入" 文檔 來(lái)入門。 需要知道的是對(duì)于 Python 運(yùn)行時(shí)系統(tǒng)來(lái)說,C 和 C++ 并不沒有太大的區(qū)別 —— 因此圍繞一個(gè) C 結(jié)構(gòu)(指針)類型構(gòu)建新 Python 對(duì)象的策略同樣適用于 C++ 對(duì)象。

有關(guān)C ++庫(kù),請(qǐng)參閱 C很難寫,有沒有其他選擇?

我使用Setup文件添加了一個(gè)模塊,為什么make失敗了??

安裝程序必須以換行符結(jié)束,如果沒有換行符,則構(gòu)建過程將失敗。 (修復(fù)這個(gè)需要一些丑陋的shell腳本編程,而且這個(gè)bug很小,看起來(lái)不值得花這么大力氣。)

如何調(diào)試擴(kuò)展??

將GDB與動(dòng)態(tài)加載的擴(kuò)展名一起使用時(shí),在加載擴(kuò)展名之前,不能在擴(kuò)展名中設(shè)置斷點(diǎn)。

在您的 .gdbinit 文件中(或交互式)添加命令:

br _PyImport_LoadDynamicModule

然后運(yùn)行GDB:

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue

我想在Linux系統(tǒng)上編譯一個(gè)Python模塊,但是缺少一些文件。為什么??

大多數(shù)打包的Python版本不包含 /usr/lib/python2.x/config/ 目錄,該目錄中包含編譯Python擴(kuò)展所需的各種文件。

對(duì)于Red Hat,安裝python-devel RPM以獲取必要的文件。

對(duì)于Debian,運(yùn)行 apt-get install python-dev 。

如何區(qū)分“輸入不完整”和“輸入無(wú)效”??

有時(shí),希望模仿Python交互式解釋器的行為,在輸入不完整時(shí)(例如,您鍵入了“if”語(yǔ)句的開頭,或者沒有關(guān)閉括號(hào)或三個(gè)字符串引號(hào)),給出一個(gè)延續(xù)提示,但當(dāng)輸入無(wú)效時(shí),立即給出一條語(yǔ)法錯(cuò)誤消息。

在Python中,您可以使用 codeop 模塊,該模塊非常接近解析器的行為。例如,IDLE就使用了這個(gè)。

在C中執(zhí)行此操作的最簡(jiǎn)單方法是調(diào)用 PyRun_InteractiveLoop() (可能在單獨(dú)的線程中)并讓Python解釋器為您處理輸入。您還可以設(shè)置 PyOS_ReadlineFunctionPointer() 指向您的自定義輸入函數(shù)。有關(guān)更多提示,請(qǐng)參閱 Modules/readline.cParser/myreadline.c 。

如何找到未定義的g++符號(hào)__builtin_new或__pure_virtual??

要?jiǎng)討B(tài)加載g ++擴(kuò)展模塊,必須重新編譯Python,要使用g ++重新鏈接(在Python Modules Makefile中更改LINKCC),及鏈接擴(kuò)展模塊(例如: g++ -shared -o mymodule.so mymodule.o )。

能否創(chuàng)建一個(gè)對(duì)象類,其中部分方法在C中實(shí)現(xiàn),而其他方法在Python中實(shí)現(xiàn)(例如通過繼承)??

是的,您可以繼承內(nèi)置類,例如 intlist , dict 等。

Boost Python庫(kù)(BPL,http://www.boost.org/libs/python/doc/index.html)提供了一種從C ++執(zhí)行此操作的方法(即,您可以使用BPL繼承自C ++編寫的擴(kuò)展類 )。