IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> go调用python3:go-python3包的使用 -> 正文阅读

[Python知识库]go调用python3:go-python3包的使用

参考资料:

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:

# github.com/DataDog/go-python3
../../../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的可能没问题):

# github.com/DataDog/go-python3
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 (以后也可能用得到):

  1. 官网下载压缩包:https://golang.google.cn/dl/
    QvtNdL
    注意选x86架构而不是ARM64
  2. 将解压的文件夹放到一个你喜欢的地方,我是/Users/xwj/sdk/go_x86_1.17.1
  3. 进入这个文件夹修改go执行文件名字,防止和本来的arm架构的go重名,并且提醒自己使用的go环境
    cd go_x86_1.17.1/bin
    mv go gox86
    
  4. 添加配置文件:
    vim ~/.zshrc,我的配置文件如下
    # Go arm64
    # export GOROOT="/Users/xwj/sdk/go1.17.1"
    # Go x86-64
    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一下即可(重开终端)
  5. 测试:image-20211006170400449请添加图片描述
    这样go环境就可以切换了,目前先使用amd64的尝试
    下面再重新get测试一下:
export PKG_CONFIG_PATH=/Users/xwj/opt/anaconda3/envs/fs_py37/lib/pkgconfig
gox86 get github.com/datadog/go-python3  

6561pH
大功告成!

二、基本使用

2.1 调用自定义python

目录总体结构:

image-20211006213827430

测试的python文件

hello.py

a = 10
def SayHello(xixi):
    return xixi + "haha"

可以将go调用python的过程分为以下5步:

  1. 初始化python环境
  2. 引入模块py对象
  3. 使用该模块的变量与函数
  4. 解析结果
  5. 销毁python3运行环境

以下五步的所有程序都在main.go文件中

package main

import (
	"fmt"
	"github.com/datadog/go-python3"
	"os"
)

func init() {
	// 1. 初始化python环境
	python3.Py_Initialize()
	if !python3.Py_IsInitialized() {
		fmt.Println("Error initializing the python interpreter")
		os.Exit(1)
	}
}

func main() {
	//2. 设置python import 的路径
	//p := "/Users/xwj/opt/anaconda3/envs/fs_py37/lib/python3.7/site-packages"
	//InsertBeforeSysPath(p)
	// 3. 导入hello模块
	hello := ImportModule("/Users/xwj/projects/blockchain_projects/FedSharing/fed_sharing/mainchain/miner/test/hello", "hello")
	// pyObject => string 解析结果
	helloRepr, err := pythonRepr(hello)
	if err != nil {
		panic(err)
	}
	fmt.Printf("[MODULE] repr(hello) = %s\n", helloRepr)
	// 4. 获取变量
	a := hello.GetAttrString("a")
	aString, err := pythonRepr(a)
	if err != nil {
		panic(err)
	}
	fmt.Printf("[VARS] a = %#v\n", aString)
	// 5. 获取函数方法
	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))
	// 6. 结束环境
	python3.Py_Finalize()
}

// InsertBeforeSysPath
// @Description: 添加site-packages路径即包的查找路径
// @param p
//func InsertBeforeSysPath(p string){
//	sysModule := python3.PyImport_ImportModule("sys")
//	path := sysModule.GetAttrString("path")
//	python3.PyList_Append(path, python3.PyBytes_FromString(p))
//}

// ImportModule
// @Description: 倒入一个包
// @param dir
// @param name
// @return *python3.PyObject
func ImportModule(dir, name string) *python3.PyObject {
	sysModule := python3.PyImport_ImportModule("sys") 	// import sys
	path := sysModule.GetAttrString("path")            // path = sys.path
	python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(dir)) // path.insert(0, dir)
	return python3.PyImport_ImportModule(name)            // return __import__(name)
}

// pythonRepr
// @Description: PyObject转换为string
// @param o
// @return string
// @return error
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
}

// PrintList
// @Description: 输出一个List
// @param list
// @return error
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

UFgcZm

原因:

该运行文件没有链接到需要匹配的python lib库

解决:

首先查看该文件的依赖文件:

otool -L /Users/xwj/projects/blockchain_projects/FedSharing/fed_sharing/mainchain/miner/test/./test

mYYj86

可以看到有两个依赖,第一个无法加载,可能就是路径有问题

在此之前,我们可以简单了解一下@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指向这个相对路径

再次运行即可

输出如下:

image-20211006215622130

2.2 调用import的第三方库包

这里以sklearn为例

在hello.py中添加import sklearn

在conda环境下下载:conda install scikit-learn -y

如果使用IDE记得将IDE中python环境设置与conda activate的环境一致

main.go中添加:

func main() {
  ...
  // 6. 调用第三方库sklearn
	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)
  ...
}	

运行结果如下:

image-20211006230529317

基本的使用可以了之后就可以自由的使用go去调用python函数了,还有更多的功能(例如设置python变量的值等)都可以在https://docs.python.org/3.7/c-api/index.html查看!

觉得不错的话,请点赞关注呦~~你的关注就是博主的动力
关注公众号,查看更多go开发、密码学和区块链科研内容:
2DrbmZ

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-10-07 13:48:31  更:2021-10-07 13:49:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 21:44:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计