go语言调用python_Go能否使用Python作为内嵌的脚本语言?

会玩Go!会玩Python!嘭!Go-Python!

PPAP大叔

Golang是静态语言,性能很好,当它不那么灵活,不好在运行时动态运行代码。Python是动态语言,非常灵活,但是性能很差。古人云:“鱼和熊掌不能兼得”。但是如今有了Go-Python,鱼和熊掌也可以兼得。

首先安装go-python

go get github.com/sbinet/go-python

我们使用下面的代码启动Python命令行解释器 go run test.go

// file test.gopackage 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)

}

代码很简单,先初始化,然后将命令行参数传递进Py_Main函数就可以进入Python命令行解释器,就像直接敲python命令一样

如果我们执行 go run main.go --version就可以查看Python版本信息

接下来我们使用golang打印一下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里面没有包含当前目录,这意味着不能直接在当前目录导入模块。

接下来我们在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模块了,先写一个斐波那契级数

# fib.py

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也就比较Ok了。

# reqbaidu.py

import requests

def touch_baidu():

res = requests.get("http://www.baidu.com")

return res

// test.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有,并且性能不是很重要

你需要一个脚本语言嵌入到go中,让go代码获得动态能力

你想使用Cython干一些hack的事但是又不想撸C语言

go-python有什么缺点python虚拟机是全局的,意味着线程不安全,在必要的地方要使用GIL保护

python会拖慢golang的性能,在性能重要的场合谨慎使用

python虚拟机会额外消耗不少内存

最后最后,不推荐使用Python语言作为Golang的内置脚本语言,至于原因,看下面的这篇文章你就明白了钱文品:GopherLua基础入门​zhuanlan.zhihu.comv2-bde8f151853cd5852bbf2681f285d5df_ipico.jpg

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值