前言
我们可以使用Go生成Python的动态链接库,这样就可以在Python中调用Go代码了,会极大的简化代码量。目前我用Go做了一个与匹配命令有关的一个函数,需要给我工作中别的使用python的小伙伴提供一个可在python直接调用的程序,python目前没有办法直接调用go的代码,需要先利用cgo将go的代码先转换一下,才能在python中调用。第一次做这个,跌跌撞撞遇到很多问题,不论是安装C的编译器还是转换数据类型等等等等(我感觉我遇上了所能遇见的各种问题…我做了很多个小demo最终一步步找到问题并解决),但是最终在python中能够调用go代码的那一刻,我露出了慈祥而又满足而又欣慰而又愉悦而又诡异的笑容😀。
使用环境介绍
- OS:window10
- Go:1.15.3
- python:3.9
处理过程
-
在自己需要导出的函数上方加上
// export 函数名
,注意这行注释一定要加上(此外要注意要有main函数,不管main函数上是否有内容,此处我的main
函数是空的~想直接在python中写调用)。
如图所示:
-
除了自己的程序需要的包以外,还需要导入
import "C"
包,因为在调用过程中需要用C来做一个媒介。如图所示:
-
如果是Windows系统,需要安装MingW64----windows版本的c/c++编译器(如果已经安装了可以跳过此步骤,不确定是否安装的话可以使用
gcc -v
在命令行中查看是否安装成功)。
下载链接(离线版):https://sourceforge.net/projects/mingw-w64/files/latest/download,
官网地址为:https://sourceforge.net/projects/mingw-w64/files/mingw-w64/
还可以点击我的网盘链接下载:https://pan.baidu.com/s/19zOjXVtmpSZffdZAQm9xLQ,提取码:5bnl
可能由于我自身对MingW64的了解不太多,这个MingW64我在官网下载的时候,自己配置时总是不太全,有同样困扰的小伙伴可以直接下载我网盘提供的安装包下载安装好,按我的步骤做就可以直接用了,我下载的是离线的(注意我的我网盘里的安装包仅适用于windows64位),解压后放到一个合适的位置,接下来就是设置环境变量:右击“我的电脑”—“属性”—“高级系统设置”—“环境变量”—选中“Path”—“系统变量”下的“编辑”:
接着点击“新建”:将bin目录路径放进去,我的路径在:D:\MinGW\mingw64\bin
在cmd中输入gcc -v,有返回的版本结果信息等即可。
-
然后回到刚才的地方,由于此处我用的是Windows,将go文件编译成dll即可,如果是Linux就编译成so(但事实上我用的windows转换成.so文件也依旧可以使用hiahia~)。语法格式为(在Terminal输入即可):
go build -buildmode=c-shared -o dll文件或者so文件 go源文件
我的执行结果为:
目前没有报错,继续往下~
就开始了我坎坷的python调用go代码之旅!!!
我在python中写了简单的调用代码:
#!/user/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
lib = ctypes.CDLL('./matchCommand.so')
str = lib.matchCommand("Linux", "factor --version")
print(str)
发现可以进入我的go文件,但是无法进入我的方法,我在网上看到很多文章都说的是go与cgo数据类型不同的问题~(我做了几个简单的小的demo,验证了确实是数据类型的问题… …)需要在go代码中先把数据类型进行转换,于是,我又开始研究官方文档了(此处省略一些很傻的坑…)看到官方文档给出了一些原始的C兼容数据类型(下图),我看见自己的go代码中传入的参数与返回值都是字符串类型,Python是利用ctypes来跟so模块进行交互,其中存在着一个代码的翻译过程,包括数据类型的翻译,如果需要传参获取接收返回值,需要在golang中将参数按照下表对应,定义成C语言的数据类型。
5. 我对自己最初的代码进行了如下调整:
- 对传入的参数进行了类型的转换
- 对返回值进行了类型转换:
注意:内部的数据类型不需要做任何改变,只需要更改传入的参数和返回值的类型即可!!我自己的方法中有很多数据类型,包括字节数组、字符串、一些切片等等。。我都转过,后来发现根本不用!!(还是我理解的不够深入…我悔过)
- 此时在python中,也不能简单的直接调用了,需要用对应的数据类型进行转换:
#!/user/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
matchCommand = ctypes.CDLL("./matchCommand.so").matchCommand
# 显式声明参数和返回值的期望类型
matchCommand.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
matchCommand.restype = ctypes.c_char_p
# 这个地方在前面加b,是代表bytes
matchCommand(b"linux", b"yum remove")
至此,执行python文件之后,就成功了。整个过程我尽量进行了详细的回忆,如果有不准确的地方,欢迎指正(#^.^#)