Go 是静态语言,性能很好,但是不那么灵活,不好在运行时动态运行代码。Python是动态语言,非常灵活,但是性能很差。古人云:“鱼和熊掌不能兼得”。但是如今有了Go-Python,鱼和熊掌也可以兼得。
注意:Go-Python 只支持 python2,暂不支持 python3。
需要安装第三方包 go-python
,GitHub 地址如下:
https://github.com/sbinet/go-python
或者使用
go get github.com/sbinet/go-python
如果 go get
或者 kg-config
失败,使用如下操作:
cd go-python
edit cgoflags.go
make VERBOSE=1
注意:需要适当的头文件和 python 开发环境。在 Debian 上,您需要安装
python-all-dev
包。
创建test1.go文件:
package main
import (
"github.com/sbinet/go-python"
"os"
)
func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}
func main() {
rc := python.Py_Main(os.Args)
os.Exit(rc)
}
运行上面的代码会启动 Python 命令行解释器 go run test1.go
,代码很简单,先初始化,然后将命令行参数传递进 Py_Main 函数就可以进入 Python 命令行解释器,就像直接敲 python 命令一样。
如果执行 go run test1.go --version
就可以查看Python版本信息。
接下来使用 go 打印一下 Python 环境的 sys.path
变量:
package main
import (
"fmt"
"github.com/sbinet/go-python"
)
func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}
func main() {
m := python.PyImport_ImportModule("sys")
if m == nil {
fmt.Println("import error")
return
}
path := m.GetAttrString("path")
if path == nil {
fmt.Println("get path error")
return
}
size := python.PyList_GET_SIZE(path)
for i := 0; i < size; i++ {
item := python.PyList_GET_ITEM(path, i)
s := python.PyString_AsString(item)
fmt.Println(s)
}
}
首先调用 PyImport_ImportModule 导入sys
包,然后取出 path
对象,再获取 path
的长度,使用循环挨个取出列表中的字符串,然后打印出来。
接下来在 sys.path
里面加入当前目录:
package main
import (
"fmt"
"github.com/sbinet/go-python"
)
func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}
func main() {
m := python.PyImport_ImportModule("sys")
if m == nil {
fmt.Println("import error")
return
}
path := m.GetAttrString("path")
if path == nil {
fmt.Println("get path error")
return
}
//加入当前目录,空串表示当前目录
currentDir := python.PyString_FromString("")
python.PyList_Insert(path, 0, currentDir)
size := python.PyList_GET_SIZE(path)
for i := 0; i < size; i++ {
item := python.PyList_GET_ITEM(path, i)
s := python.PyString_AsString(item)
fmt.Println(s)
}
}
在sys.path
列表的头部插入了空串,表示将当前目录加入sys.path
,于是当前目录成为优先查找路径。
有了上面的代码,可以试一试调用自定义 python
模块了,先写一个斐波那契级数:
def fib(n):
if n <= 2:
return 1
return fib(n-1) + fib(n-2)
这是一个递归版本的实现,n 的大小不能超过最大栈深,然后使用 go 调用:
package main
import (
"fmt"
"github.com/sbinet/go-python"
)
func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}
func main() {
m := python.PyImport_ImportModule("sys")
if m == nil {
fmt.Println("import error")
return
}
path := m.GetAttrString("path")
if path == nil {
fmt.Println("get path error")
return
}
//加入当前目录,空串表示当前目录
currentDir := python.PyString_FromString("")
python.PyList_Insert(path, 0, currentDir)
m = python.PyImport_ImportModule("fib")
if m == nil {
fmt.Println("import error")
return
}
fib := m.GetAttrString("fib")
if fib == nil {
fmt.Println("get fib error")
return
}
out := fib.CallFunction(python.PyInt_FromLong(10))
if out == nil {
fmt.Println("call fib error")
return
}
fmt.Printf("fib(%d)=%d\n", 10, python.PyInt_AsLong(out))
}
因为当前目录已经插入sys.path
,我们可以直接使用 PyImport_ImportModule 导入 fib 模块,然后获取 fib 函数对象,注意函数也是一个 PyObject 对象。将整数 10 传递进 fib 函数,得到结果打印出来。
接下来尝试在自定义模块里使用 requests 访问百度首页,如果能使用第三方 Python 模块,那么 go-python 就可以了。
python 代码如下:
import requests
def touch_baidu():
res = requests.get("http://www.baidu.com")
return res
go 代码:
package main
import (
"fmt"
"github.com/sbinet/go-python"
)
func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}
func main() {
m := python.PyImport_ImportModule("sys")
if m == nil {
fmt.Println("import error")
return
}
path := m.GetAttrString("path")
if path == nil {
fmt.Println("get path error")
return
}
//加入当前目录,空串表示当前目录
currentDir := python.PyString_FromString("")
python.PyList_Insert(path, 0, currentDir)
m = python.PyImport_ImportModule("reqbaidu")
if m == nil {
fmt.Println("import error")
return
}
touchBaidu := m.GetAttrString("touch_baidu")
if touchBaidu == nil {
fmt.Println("get touch_baidu error")
return
}
res := touchBaidu.CallFunction()
if res == nil {
fmt.Println("call touch_baidu error")
return
}
statusCode := res.GetAttrString("status_code")
content := res.GetAttrString("content")
fmt.Println(python.PyInt_AS_LONG(statusCode))
fmt.Println(python.PyString_AS_STRING(content))
}
touchBaidu 返回的是一个 requests.Response 对象,该对象里的属性 status_code 表示返回状态码,content 属性表示返回内容。
何时使用go-python
go-python有什么缺点