参考资料:
https://zhuanlan.zhihu.com/p/150253406
https://blog.csdn.net/skyztttt/article/details/8115086
包地址:
python2:https://github.com/sbinet/go-python
python3:https://github.com/DataDog/go-python3
Python是机器/深度学习御用开发语言,Golang是新时代后端开发语言。Python很适合算法写模型,而Golang很适合提供API服务,两位同志都红的发紫。
出于项目需求和兴趣,这里就介绍一下正确搅基的办法。
从网络中查询资料后,发现两个好用的包(见上方)
因为python使用的是3.x,所以本文使用的是DataDog/go-python3版本, sbinet/go-python对应python2.x
一、安装go-python3包
安装就有点麻烦。。。
我的环境
系统:MacOS Big Sur 11.6 M1芯片 arm64
go:go version go1.16.5 darwin/arm64
python:python3.9
命令: go get github.com/DataDog/go-python3
报错1:
../../../go_projects/pkg/mod/github.com/!data!dog/go-python3@v0.0.0-20210805105248-03d93fb21b67/dict.go:141:13: could not determine kind of name for C.PyDict_ClearFreeList
原因:
DataDog/go-python3 只适用于python3.7 版本!,所以之前我的电脑安装的是3.9,3.9版本删除了C.PyDict_ClearFreeList 函数,所以找不到。
(The C.PyDict_ClearFreeList function has been removed from the Python C API with Python 3.9)
https://github.com/DataDog/go-python3/issues/38
解决:
使用anaconda 创建一个python3.7的环境:(如果不会使用anaconda 可以自行百度,不难)
conda create -n fs_py37 python=3.7
conda activate fs_py37
找到环境下lib/pkgconfig 目录, 我的是:(一般都在conda安装中的envs下)
/Users/xwj/opt/anaconda3/envs/fs_py37/lib/pkgconfig
然后重置环境, go get:
export PKG_CONFIG_PATH=/Users/xwj/opt/anaconda3/envs/fs_py37/lib/pkgconfig
go get github.com/DataDog/go-python3
报错2(不是m1 mac的可能没问题):
ld: warning: ignoring file /Users/xwj/opt/anaconda3/envs/fs_py37/lib/libpython3.7m.dylib, building for macOS-arm64 but attempting to link with file built for macOS-x86_64
Undefined symbols for architecture arm64:
"_PyBool_FromLong", referenced from:
__cgo_a0c8609171f9_Cfunc_PyBool_FromLong in _x002.o
(maybe you meant: __cgo_a0c8609171f9_Cfunc_PyBool_FromLong)
.....
原因:
anaconda安装的python3.7版本(amd64)与go的arm64架构不匹配(垃圾M1)
解决:
要么换python架构,要么换go的架构,反正两者架构要一致才可以
尴尬的是,目前M1还是不支持python@3.7: https://doesitarm.com/formula/python@3.7/
所以只能安装一个go的amd64版本也就是intel的x86 (以后也可能用得到):
- 官网下载压缩包:https://golang.google.cn/dl/
注意选x86架构而不是ARM64 - 将解压的文件夹放到一个你喜欢的地方,我是
/Users/xwj/sdk/go_x86_1.17.1 - 进入这个文件夹修改
go 执行文件名字,防止和本来的arm架构的go 重名,并且提醒自己使用的go环境cd go_x86_1.17.1/bin
mv go gox86
- 添加配置文件:
vim ~/.zshrc ,我的配置文件如下
export GOROOT="/Users/xwj/sdk/go_x86_1.17.1"
export GOPROXY=https://goproxy.cn,direct
export GOPATH="/Users/xwj/projects/go_projects"
export GO111MODULE="auto"
export PATH=$PATH:$GOROOT/bin:$GOPATH/src:$GOPATH/bin
source ~/.zshrc , 重新打开一个终端 想使用哪个就将另一个注释掉再source一下即可(重开终端) - 测试:
这样go环境就可以切换了,目前先使用amd64的尝试 下面再重新get测试一下:
export PKG_CONFIG_PATH=/Users/xwj/opt/anaconda3/envs/fs_py37/lib/pkgconfig
gox86 get github.com/datadog/go-python3
大功告成!
二、基本使用
2.1 调用自定义python
目录总体结构:
测试的python文件
hello.py
a = 10
def SayHello(xixi):
return xixi + "haha"
可以将go调用python的过程分为以下5步:
- 初始化python环境
- 引入模块py对象
- 使用该模块的变量与函数
- 解析结果
- 销毁python3运行环境
以下五步的所有程序都在main.go 文件中
package main
import (
"fmt"
"github.com/datadog/go-python3"
"os"
)
func init() {
python3.Py_Initialize()
if !python3.Py_IsInitialized() {
fmt.Println("Error initializing the python interpreter")
os.Exit(1)
}
}
func main() {
hello := ImportModule("/Users/xwj/projects/blockchain_projects/FedSharing/fed_sharing/mainchain/miner/test/hello", "hello")
helloRepr, err := pythonRepr(hello)
if err != nil {
panic(err)
}
fmt.Printf("[MODULE] repr(hello) = %s\n", helloRepr)
a := hello.GetAttrString("a")
aString, err := pythonRepr(a)
if err != nil {
panic(err)
}
fmt.Printf("[VARS] a = %#v\n", aString)
SayHello := hello.GetAttrString("SayHello")
args := python3.PyTuple_New(1)
python3.PyTuple_SetItem(args, 0, python3.PyUnicode_FromString("xwj"))
res := SayHello.Call(args, python3.Py_None)
fmt.Printf("[FUNC] res = %s\n", python3.PyUnicode_AsUTF8(res))
python3.Py_Finalize()
}
func ImportModule(dir, name string) *python3.PyObject {
sysModule := python3.PyImport_ImportModule("sys")
path := sysModule.GetAttrString("path")
python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(dir))
return python3.PyImport_ImportModule(name)
}
func pythonRepr(o *python3.PyObject) (string, error) {
if o == nil {
return "", fmt.Errorf("object is nil")
}
s := o.Repr()
if s == nil {
python3.PyErr_Clear()
return "", fmt.Errorf("failed to call Repr object method")
}
defer s.DecRef()
return python3.PyUnicode_AsUTF8(s), nil
}
func PrintList(list *python3.PyObject) error {
if exc := python3.PyErr_Occurred(); list == nil && exc != nil {
return fmt.Errorf("Fail to create python list object")
}
defer list.DecRef()
repr, err := pythonRepr(list)
if err != nil {
return fmt.Errorf("fail to get representation of object list")
}
fmt.Printf("python list: %s\n", repr)
return nil
}
编译成可执行文件test 并运行:
gox86 build ./
./test
运行时报错:dyld: Library not loaded: @rpath/libpython3.7m.dylib
原因:
该运行文件没有链接到需要匹配的python lib库
解决:
首先查看该文件的依赖文件:
otool -L /Users/xwj/projects/blockchain_projects/FedSharing/fed_sharing/mainchain/miner/test/./test
可以看到有两个依赖,第一个无法加载,可能就是路径有问题
在此之前,我们可以简单了解一下@executable_path、@loader_path、@rpath 三者都是什么意思:
- @executable_path:就是可执行程序的路径
- @loader_path:可以通过这个path来设置动态库的install path name
- @rpath:它是run path的缩写。本质上它不是一个明确的path,甚至可以说它不是一个path。它只是一个变量,或者叫占位符。这个变量通过XCode中的run path选项设置值,或者通过
install_name_tool 的-add_rpath 设置值。设置好run path之后,所有的@rpath都会被替换掉
我们并没有设置@rpath,所以导致失败。
所以在这里我们给其设置一个@rpath即之前本地conda创建的库,这样就可以链接上了。
找到库的路径,我的是/Users/xwj/opt/anaconda3/envs/fs_py37/lib/libpython3.7m.dylib (就是在你创建的环境名下的lib中)
然后对这个可执行文件test 运行以下命令添加:
install_name_tool -add_rpath /Users/xwj/opt/anaconda3/envs/fs_py37/lib ./test
如果你的可执行程序希望在多个机器上都适配,那么可以添加一个链接,把lib放到项目相对路径下,使用@executable_path指向这个相对路径
再次运行即可
输出如下:
2.2 调用import的第三方库包
这里以sklearn 为例
在hello.py中添加import sklearn
在conda环境下下载:conda install scikit-learn -y
如果使用IDE记得将IDE中python环境设置与conda activate的环境一致
在main.go 中添加:
func main() {
...
sklearn := hello.GetAttrString("sklearn")
skVersion := sklearn.GetAttrString("__version__")
sklearnRepr, err := pythonRepr(sklearn)
if err != nil {
panic(err)
}
skVersionRepr, err := pythonRepr(skVersion)
if err != nil {
panic(err)
}
fmt.Printf("[IMPORT] sklearn = %s\n", sklearnRepr)
fmt.Printf("[IMPORT] sklearn version = %s\n", skVersionRepr)
...
}
运行结果如下:
基本的使用可以了之后就可以自由的使用go去调用python函数了,还有更多的功能(例如设置python变量的值等)都可以在https://docs.python.org/3.7/c-api/index.html查看!
觉得不错的话,请点赞关注呦~~你的关注就是博主的动力 关注公众号,查看更多go开发、密码学和区块链科研内容:
|