专栏首页程序员与猫Golang 调用 Python 代码

Golang 调用 Python 代码

go 中的 cgo 模块可以让 go 无缝调用 c 或者 c++ 的代码,而 python 本身就是个 c 库,自然也可以由 cgo 直接调用,前提是指定正确的编译条件,如 Python.h 头文件(),以及要链接的库文件。本文以 Ubuntu 18.04 作为开发和运行平台进行演示。

其实在使用 cgo 之前,笔者也考虑过使用 grpc 的方式。比如可以将需要调用的 python 代码包装成一个 grpc server 端,然后再使用 go 编写对应的 client 端,这样考虑的前提是,go 调用 python 代码本来就是解一时之困,而且引入语言互操作后,对于项目维护和开发成本控制都有不小的影响,如果直接使用 grpc 生成编程语言无感知的协议文件,将来无论是重构或使用其他语言替换 python 代码,都是更加方便,也是更加解耦的。所以 grpc 也是一种比较好的选择。至于通信延迟,老实说既然已经设计语言互操作,本机中不到毫秒级的损失其实也是可以接受的。

接下来进入正题。

1. 针对 python 版本安装 python-dev

sudo apt install python3.6-dev

系统未默认安装 python3.x 的开发环境,所以假如要通过 cgo 调用 python,需要安装对应版本的开发包。

2. 指定对应的cgo CFLAGS 和 LDFLAGS 选项

对于未由 c 包装的 python 代码,python-dev 包中内置了 python-config 工具用于查看编译选项。

python3.6-config --cflags

python3.6-config --ldflags

以下是对应的输出

-I/usr/include/python3.6m -I/usr/include/python3.6m  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.6-MtRqCA/python3.6-3.6.6=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall

-L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl  -lutil -lm  -xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

低版本的 python 也可以在安装开发包后,使用对应的 python-config 命令打印依赖配置。由于 cgo 默认使用的编译器不是 gcc ,所以输出中的部分选项并不受支持,所以最后 cgo 代码的配置为

//#cgo CFLAGS : -I./ -I/usr/include/python3.6m
//#cgo LDFLAGS: -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl  -lutil -lm
//#include "Python.h"
import "C"

3. 部分示例代码

3.0 映射 PyObject

type PyObject struct {
    ptr *C.PyObject
}

func togo(obj *C.PyObject) *PyObject {
    if obj == nil {
        return nil
    }
    return &PyObject{ptr: obj}
}

func topy(self *PyObject) *C.PyObject {
    if self == nil {
        return nil
    }
    return self.ptr
}

3.1 python 环境的启动与终结

func Initialize() error {
    if C.Py_IsInitialized() == 0 {
        C.Py_Initialize()
    }
    if C.Py_IsInitialized() == 0 {
        return fmt.Errorf("python: could not initialize the python interpreter")
    }

    if C.PyEval_ThreadsInitialized() == 0 {
        C.PyEval_InitThreads()
    }
    if C.PyEval_ThreadsInitialized() == 0 {
        return fmt.Errorf("python: could not initialize the GIL")
    }

    return nil
}

func Finalize() error {
    C.Py_Finalize()
    return nil
}

3.2 包路径与模块导入

func InsertExtraPackageModule(dir string) *PyObject {
    sysModule := ImportModule("sys")
    path := sysModule.GetAttrString("path")

    cstr := C.CString(dir)
    defer C.free(unsafe.Pointer(cstr))
    C.PyList_Insert(topy(path), C.Py_ssize_t(0), topy(togo(C.PyBytes_FromString(cstr))))

    return ImportModule(dir)
}

func ImportModule(name string) *PyObject {
    c_name := C.CString(name)
    defer C.free(unsafe.Pointer(c_name))
    return togo(C.PyImport_ImportModule(c_name))
}

func (self *PyObject) GetAttrString(attr_name string) *PyObject {
    c_attr_name := C.CString(attr_name)
    defer C.free(unsafe.Pointer(c_attr_name))
    return togo(C.PyObject_GetAttrString(self.ptr, c_attr_name))
}

3.3 数据类型转换

func PyStringFromGoString(v string) *PyObject {
    cstr := C.CString(v)
    defer C.free(unsafe.Pointer(cstr))
    return togo(C.PyBytes_FromString(cstr))
}

func PyStringAsGoString(self *PyObject) string {
    c_str := C.PyBytes_AsString(self.ptr)
    return C.GoString(c_str)
}

...

可以看到形似 C.Py* 的方法都是由 cgo 模块编译调用的,这些方法也是 python 暴露的 C-API,而这里的示例就到此为止,其他诸如调用 python 模块方法的功能文档里也描述得十分详细,尽管实施起来仍然有些麻烦。

但是请注意 C-API 的 2.x 与 3.x 版本仍有不同,比如 2.x 版本中的字符串操作类型 PyString_* 在 3.x 中便被重命名为 PyBytes_*

关注过 go 与 python 互操作功能的同学应该注意到上述的示例代码部分来自 go-python 这个开源项目,有兴趣的同学也可以关注一下。 这个项目基于 python2.7 ,其中暴露的 api 诸如字符串转换也是基于 python2.x 版本,所以针对于更流行的 python3.x 项目,大家就需要自己按照上文方法做一些修改了。

实际工作中,语言的互操作场景确实很让人感觉头疼,而 cgo 的文档资料其实并不多,所以希望本文能给大家带来一些帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ASP.NET Core的Kestrel服务器

    原文地址----Kestrel server for ASP.NET Core By Tom Dykstra, Chris Ross, and Stephen ...

    潘成涛
  • EntityFramework Core 学习扫盲

    0. 写在前面 本篇文章虽说是入门学习,但是也不会循规蹈矩地把EF1.0版本一直到现在即将到来的EF Core 2.0版本相关的所有历史和细节完完整整还原出来...

    潘成涛
  • ASP.NET Core WebListener 服务器

    原文地址:WebListener server for ASP.NET Core By Tom Dykstra, Chris Ross WebListener是...

    潘成涛
  • 使用 Kotlin+RocketMQ 实现延时消息

    我们的系统完成某项操作之后,会推送事件消息到业务方的接口。当我们调用业务方的通知接口返回值为成功时,表示本次推送消息成功;当返回值为失败时,则会多次推送消息,直...

    fengzhizi715
  • Oracle压缩黑科技(二)—压缩数据的修改

    原文链接 https://www.red-gate.com/simple-talk/sql/oracle/compression-in-oracle-part-...

    沃趣科技
  • Django-DRF | APIView 视图类

    Django REST框架构建Web API。Django网络应用开发的5项基础核心技术包括模型(Model)的设计,URL 的设计与配置,View(视图)的编...

    小团子
  • 08.【Kevin聊敏捷】敏捷项目管理之Scrum价值

    很多公司虽然表面上说推行敏捷管理,他们也有PO,也有SM,每天站立会,一个迭代完了也有迭代回顾会。但是这只是表面的东西。如果推行的敏捷管理,不具备Scrum所述...

    开心的Kevin
  • 洛谷U16574 attack的斐波那契

    题目背景 attack很喜欢斐波那契数列 题目描述 设f[i]表示斐波那契数论的第i项 f[1]=1 ,f[2] =2 给定一个n 求 输入输出格式 输入...

    attack
  • 计算机视觉中,有哪些比较好的目标跟踪算法?(下)

    相信很多来这里的人和我第一次到这里一样,都是想找一种比较好的目标跟踪算法,或者想对目标跟踪这个领域有比较深入的了解,虽然这个问题是经典目标跟踪算法,但事实上,可...

    AI研习社
  • 初学C语言的学习计划

    背景:很多同学在学习C语言的过程中,常常会遇到这样的问题,即“教材看完了,知识点也懂,但写不出来程序”,这段时间,我们通过长期与有多年C语言研究经验的教授、教师...

    编程范 源代码公司

扫码关注云+社区

领取腾讯云代金券