擴展/嵌入常見(jiàn)問(wèn)題?
可以使用 C 語(yǔ)言創(chuàng )建自己的函數嗎??
是的,您可以在C中創(chuàng )建包含函數、變量、異常甚至新類(lèi)型的內置模塊。在文檔 擴展和嵌入 Python 解釋器 中有說(shuō)明。
大多數中級或高級的Python書(shū)籍也涵蓋這個(gè)主題。
可以使用 C++ 語(yǔ)言創(chuàng )建自己的函數嗎??
是的,可以使用C ++中兼容C的功能。 在Python include文件周?chē)胖胉 extern“C”{...}` ,并在Python解釋器調用的每個(gè)函數之前放置 extern“C”
。 具有構造函數的全局或靜態(tài)C ++對象可能不是一個(gè)好主意。
C很難寫(xiě),有沒(méi)有其他選擇??
編寫(xiě)自己的C擴展有很多選擇,具體取決于您要做的事情。
Cython 及其相關(guān)的 Pyrex 是接受稍微修改過(guò)的Python形式并生成相應C代碼的編譯器。 Cython和Pyrex可以編寫(xiě)擴展而無(wú)需學(xué)習Python的C API。
如果需要連接到某些當前不存在Python擴展的C或C ++庫,可以嘗試使用 SWIG 等工具包裝庫的數據類(lèi)型和函數。 SIP , CXX Boost , 或 Weave 也是包裝C ++庫的替代方案。
如何在 C 中執行任意 Python 語(yǔ)句??
執行此操作的最高層級函數為 PyRun_SimpleString()
,它接受單個(gè)字符串參數用于在模塊 __main__
的上下文中執行并在成功時(shí)返回 0
而在發(fā)生異常 (包括 SyntaxError
) 時(shí)返回 -1
。 如果你想要更多可控性,可以使用 PyRun_String()
;請在 Python/pythonrun.c
中查看 PyRun_SimpleString()
的源碼。
如何在 C 中對任意 Python 表達式求值??
可以調用前一問(wèn)題中介紹的函數 PyRun_String()
并附帶起始標記符 Py_eval_input
;它會(huì )解析表達式,對其求值并返回結果值。
如何從Python對象中提取C的值??
這取決于對象的類(lèi)型。 如果是元組,PyTuple_Size()
可返回其長(cháng)度而 PyTuple_GetItem()
可返回指定序號上的項。 對于列表也有類(lèi)似的函數 PyListSize()
和 PyList_GetItem()
。
對于字節串,PyBytes_Size()
可返回其長(cháng)度而 PyBytes_AsStringAndSize()
提供一個(gè)指向其值和長(cháng)度的指針。 請注意 Python 字節串可能為空,因此 C 的 strlen()
不應被使用。
要檢測一個(gè)對象的類(lèi)型,首先要確保它不為 NULL
,然后使用 PyBytes_Check()
, PyTuple_Check()
, PyList_Check()
等等。
還有一個(gè)針對 Python 對象的高層級 API,通過(guò)所謂的‘抽象’接口提供 —— 請參閱 Include/abstract.h
了解詳情。 它允許使用 PySequence_Length()
, PySequence_GetItem()
這樣的調用來(lái)與任意種類(lèi)的 Python 序列進(jìn)行對接,此外還可使用許多其他有用的協(xié)議例如數字 (PyNumber_Index()
等) 以及 PyMapping API 中的各種映射等等。
如何使用Py_BuildValue()創(chuàng )建任意長(cháng)度的元組??
不可以。應該使用 PyTuple_Pack()
。
如何從C調用對象的方法??
可以使用 PyObject_CallMethod()
函數來(lái)調用某個(gè)對象的任意方法。 形參為該對象、要調用的方法名、類(lèi)似 Py_BuildValue()
所用的格式字符串以及要傳給方法的參數值:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
這適用于任何具有方法的對象 —— 不論是內置方法還是用戶(hù)自定義方法。 你需要負責對返回值進(jìn)行最終的 Py_DECREF()
處理。
例如調用某個(gè)文件對象的 "seek" 方法并傳入參數 10, 0 (假定文件對象的指針為 "f"):
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
... an exception occurred ...
}
else {
Py_DECREF(res);
}
請注意由于 PyObject_CallObject()
總是 接受一個(gè)元組作為參數列表,要調用不帶參數的函數,則傳入格式為 "()",要調用只帶一個(gè)參數的函數,則應將參數包含于圓括號中,例如 "(i)"。
如何捕獲PyErr_Print()(或打印到stdout / stderr的任何內容)的輸出??
在 Python 代碼中,定義一個(gè)支持 write()
方法的對象。 將此對象賦值給 sys.stdout
和 sys.stderr
。 調用 print_error 或者只是允許標準回溯機制生效。 在此之后,輸出將轉往你的 write()
方法所指向的任何地方。
做到這一點(diǎn)的最簡(jiǎn)單方式是使用 io.StringIO
類(lèi):
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!
實(shí)現同樣效果的自定義對象看起來(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訪(fǎng)問(wèn)用Python編寫(xiě)的模塊??
你可以通過(guò)如下方式獲得一個(gè)指向模塊對象的指針:
module = PyImport_ImportModule("<modulename>");
如果模塊尚未被導入(即它還不存在于 sys.modules
中),這會(huì )初始化該模塊;否則它只是簡(jiǎn)單地返回 sys.modules["<modulename>"]
的值。 請注意它并不會(huì )將模塊加入任何命名空間 —— 它只是確保模塊被初始化并存在于 sys.modules
中。
之后你就可以通過(guò)如下方式來(lái)訪(fǎng)問(wèn)模塊的屬性(即模塊中定義的任何名稱(chēng)):
attr = PyObject_GetAttrString(module, "<attrname>");
調用 PyObject_SetAttrString()
為模塊中的變量賦值也是可以的。
如何在 Python 中對接 C ++ 對象??
根據你的需求,可以選擇許多方式。 手動(dòng)的實(shí)現方式請查閱 "擴展與嵌入" 文檔 來(lái)入門(mén)。 需要知道的是對于 Python 運行時(shí)系統來(lái)說(shuō),C 和 C++ 并不沒(méi)有太大的區別 —— 因此圍繞一個(gè) C 結構(指針)類(lèi)型構建新 Python 對象的策略同樣適用于 C++ 對象。
有關(guān)C ++庫,請參閱 C很難寫(xiě),有沒(méi)有其他選擇?
我使用Setup文件添加了一個(gè)模塊,為什么make失敗了??
安裝程序必須以換行符結束,如果沒(méi)有換行符,則構建過(guò)程將失敗。 (修復這個(gè)需要一些丑陋的shell腳本編程,而且這個(gè)bug很小,看起來(lái)不值得花這么大力氣。)
如何調試擴展??
將GDB與動(dòng)態(tài)加載的擴展名一起使用時(shí),在加載擴展名之前,不能在擴展名中設置斷點(diǎn)。
在您的 .gdbinit
文件中(或交互式)添加命令:
br _PyImport_LoadDynamicModule
然后運行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系統上編譯一個(gè)Python模塊,但是缺少一些文件。為什么??
大多數打包的Python版本不包含 /usr/lib/python2.x/config/
目錄,該目錄中包含編譯Python擴展所需的各種文件。
對于Red Hat,安裝python-devel RPM以獲取必要的文件。
對于Debian,運行 apt-get install python-dev
。
如何區分“輸入不完整”和“輸入無(wú)效”??
有時(shí),希望模仿Python交互式解釋器的行為,在輸入不完整時(shí)(例如,您鍵入了“if”語(yǔ)句的開(kāi)頭,或者沒(méi)有關(guān)閉括號或三個(gè)字符串引號),給出一個(gè)延續提示,但當輸入無(wú)效時(shí),立即給出一條語(yǔ)法錯誤消息。
在Python中,您可以使用 codeop
模塊,該模塊非常接近解析器的行為。例如,IDLE就使用了這個(gè)。
在C中執行此操作的最簡(jiǎn)單方法是調用 PyRun_InteractiveLoop()
(可能在單獨的線(xiàn)程中)并讓Python解釋器為您處理輸入。您還可以設置 PyOS_ReadlineFunctionPointer()
指向您的自定義輸入函數。有關(guān)更多提示,請參閱 Modules/readline.c
和 Parser/myreadline.c
。
如何找到未定義的g++符號__builtin_new或__pure_virtual??
要動(dòng)態(tài)加載g ++擴展模塊,必須重新編譯Python,要使用g ++重新鏈接(在Python Modules Makefile中更改LINKCC),及鏈接擴展模塊(例如: g++ -shared -o mymodule.so mymodule.o
)。
能否創(chuàng )建一個(gè)對象類(lèi),其中部分方法在C中實(shí)現,而其他方法在Python中實(shí)現(例如通過(guò)繼承)??
是的,您可以繼承內置類(lèi),例如 int
, list
, dict
等。
Boost Python庫(BPL,http://www.boost.org/libs/python/doc/index.html)提供了一種從C ++執行此操作的方法(即,您可以使用BPL繼承自C ++編寫(xiě)的擴展類(lèi) )。