前言
Python是時髦的機器學習御用開發(fā)語言,Golang是大紅大紫的新時代后端開發(fā)語言。Python很適合讓搞算法的寫寫模型,而Golang很適合提供API服務(wù),兩位同志都紅的發(fā)紫,這里就介紹一下正確攪基的辦法。
go 中的 cgo 模塊可以讓 go 無縫調(diào)用 c 或者 c++ 的代碼,而 python 本身就是個 c 庫,自然也可以由 cgo 直接調(diào)用,前提是指定正確的編譯條件,如 Python.h 頭文件(),以及要鏈接的庫文件。本文以 Ubuntu 18.04 作為開發(fā)和運行平臺進行演示。
其實在使用 cgo 之前,筆者也考慮過使用 grpc 的方式。比如可以將需要調(diào)用的 python 代碼包裝成一個 grpc server 端,然后再使用 go 編寫對應(yīng)的 client 端,這樣考慮的前提是,go 調(diào)用 python 代碼本來就是解一時之困,而且引入語言互操作后,對于項目維護和開發(fā)成本控制都有不小的影響,如果直接使用 grpc 生成編程語言無感知的協(xié)議文件,將來無論是重構(gòu)或使用其他語言替換 python 代碼,都是更加方便,也是更加解耦的。所以 grpc 也是一種比較好的選擇。至于通信延遲,老實說既然已經(jīng)設(shè)計語言互操作,本機中不到毫秒級的損失其實也是可以接受的。
接下來進入正題。
Golang調(diào)用Python代碼
1. 針對 python 版本安裝 python-dev
1
|
sudo apt install python3.6-dev |
系統(tǒng)未默認安裝 python3.x 的開發(fā)環(huán)境,所以假如要通過 cgo 調(diào)用 python,需要安裝對應(yīng)版本的開發(fā)包。
2. 指定對應(yīng)的cgo CFLAGS 和 LDFLAGS 選項
對于未由 c 包裝的 python 代碼,python-dev 包中內(nèi)置了 python-config 工具用于查看編譯選項。
1
2
3
|
python3.6-config --cflags python3.6-config --ldflags |
以下是對應(yīng)的輸出
-I/usr/include/python3.6m -I/usr/include/python3.6m -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.6-MtRqCA/python3.6-3.6.6=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O3 -Wall
-L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm -xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
低版本的 python 也可以在安裝開發(fā)包后,使用對應(yīng)的 python-config 命令打印依賴配置。由于 cgo 默認使用的編譯器不是 gcc ,所以輸出中的部分選項并不受支持,所以最后 cgo 代碼的配置為
//#cgo CFLAGS : -I./ -I/usr/include/python3.6m
//#cgo LDFLAGS: -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm
//#include "Python.h"
import "C"
3. 部分示例代碼
3.0 映射 PyObject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type PyObject struct { ptr * C.PyObject } func togo(obj * C.PyObject) * PyObject { if obj = = nil { return nil } return &PyObject{ptr: obj} } func topy( self * PyObject) * C.PyObject { if self = = nil { return nil } return self .ptr } |
3.1 python 環(huán)境的啟動與終結(jié)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
func Initialize() error { if C.Py_IsInitialized() = = 0 { C.Py_Initialize() } if C.Py_IsInitialized() = = 0 { return fmt.Errorf( "python: could not initialize the python interpreter" ) } if C.PyEval_ThreadsInitialized() = = 0 { C.PyEval_InitThreads() } if C.PyEval_ThreadsInitialized() = = 0 { return fmt.Errorf( "python: could not initialize the GIL" ) } return nil } func Finalize() error { C.Py_Finalize() return nil } |
3.2 包路徑與模塊導入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
func InsertExtraPackageModule( dir string) * PyObject { sysModule : = ImportModule( "sys" ) path : = sysModule.GetAttrString( "path" ) cstr : = C.CString( dir ) defer C.free(unsafe.Pointer(cstr)) C.PyList_Insert(topy(path), C.Py_ssize_t( 0 ), topy(togo(C.PyBytes_FromString(cstr)))) return ImportModule( dir ) } func ImportModule(name string) * PyObject { c_name : = C.CString(name) defer C.free(unsafe.Pointer(c_name)) return togo(C.PyImport_ImportModule(c_name)) } func ( self * PyObject) GetAttrString(attr_name string) * PyObject { c_attr_name : = C.CString(attr_name) defer C.free(unsafe.Pointer(c_attr_name)) return togo(C.PyObject_GetAttrString( self .ptr, c_attr_name)) } |
3.3 數(shù)據(jù)類型轉(zhuǎn)換
1
2
3
4
5
6
7
8
9
10
11
12
|
func PyStringFromGoString(v string) * PyObject { cstr : = C.CString(v) defer C.free(unsafe.Pointer(cstr)) return togo(C.PyBytes_FromString(cstr)) } func PyStringAsGoString( self * PyObject) string { c_str : = C.PyBytes_AsString( self .ptr) return C.GoString(c_str) } ... |
可以看到形似 C.Py* 的方法都是由 cgo 模塊編譯調(diào)用的,這些方法也是 python 暴露的C-API ,而這里的示例就到此為止,其他諸如調(diào)用 python 模塊方法的功能文檔里也描述得十分詳細,盡管實施起來仍然有些麻煩。
但是請注意 C-API 的 2.x 與 3.x 版本仍有不同,比如 2.x 版本中的字符串操作類型 PyString_* 在 3.x 中便被重命名為 PyBytes_* 。
關(guān)注過 go 與 python 互操作功能的同學應(yīng)該注意到上述的示例代碼部分來自 go-python 這個開源項目,有興趣的同學也可以關(guān)注一下。 這個項目基于 python2.7 ,其中暴露的 api 諸如字符串轉(zhuǎn)換也是基于 python2.x 版本,所以針對于更流行的 python3.x 項目,大家就需要自己按照上文方法做一些修改了。
實際工作中,語言的互操作場景確實很讓人感覺頭疼,而 cgo 的文檔資料其實并不多,所以希望本文能給大家?guī)硪恍椭?/p>
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:http://www.cnblogs.com/Wddpct/p/9784037.html