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 的文档资料其实并不多,所以希望本文能给大家带来一些帮助。

原文地址:https://www.cnblogs.com/Wddpct/p/9784037.html

时间: 2024-11-10 11:18:42

Golang 调用 Python 代码的相关文章

如何在Java中调用Python代码

有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调用对方的代码”. 下面我将举一些简单的小例子,借此说明:如何在Java中调用Python代码. 看懂这篇文章只需要具备: 熟悉Java的基本语法 懂一点点Python 主要内容如下: 什么是Jython? 一个HelloPython程序 在Jvm中执行Python脚本 仅在Java中调用Python

『Python CoolBook』C扩展库_其六_从C语言中调用Python代码

一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILState_Ensure) 确保fun对象可调用 参数转换为python对应类型(Py_BuildValue) 调用python函数(PyObject_Call) 确定调用无异常 检查返回值 释放GIL(PyGILState_Release) 异常处理 #include "Python.h" /* Execut

C#调用Python代码

c#利用IronPython调用python的过程种种问题 - monkeyfx - 博客园 https://www.cnblogs.com/monkeyfx/p/6522000.html c#调用python脚本 - 时嬴政 - 博客园 https://www.cnblogs.com/shiyingzheng/p/6054835.html C#调用Python脚本并使用Python的第三方模块 - 流浪的松竹 - 博客园 https://www.cnblogs.com/jason2004/p/

MacOS Terminal调用Python代码

Mac基础 打开Terminal,使用cd切换路径,因为安装了xampp环境,以此为例 cd /Applications/XAMPP/xamppfiles/htdocs/python 进入路径后,查看这个文件夹下有什么文件: ls -l 运行这个.py文件: python text.py -s xxx 顺便吐槽一下,mac真美.

c#中调用python

1. 安装IronPython,到http://ironpython.codeplex.com/下载安装包 2. 创建项目 创建一个C#的控制台应用程序. 添加引用: 浏览到IronPython的安装目录中,添加对IronPython.dll,Microsoft.Scripting.dll 两个dll的引用. 3. 添加Python文件到当前的项目中 创建一个文本文件命名为:hello.py, 编辑如下 def welcome(name):    return "hello" + na

QT中 使用c++调用python

最近在做一个项目,开发环境用的是QT c++.项目中使用amazon云服务,调研发现有一个Amazon云的python接口.就有了标题中的问题,需要用C++来调用python脚本. 下面是一个c++调用python的小例子.我先贴出代码,然后在详细介绍. # -*- coding: cp936 -*- #定义hello函数,这个函数的功能是输出“hello world!” def hello(): print("hello world!") 上面就是我们一会要用C++调用python脚

Java调用Python程序

最近,需要在应用中,需要使用Java程序去调用Python代码,主要有两种实现方式. 一.使用Jython架包 因为调用的Python代码中需要使用Json对象,开始使用jython2.5.2,发现不支持json.因此, 升级了最新的Jython2.7.0,发现可以支持.因此,要使用更多python库,建议使用更高版本的Jython包. 首先,添加Jython的Maven依赖: 其后,写了一个简单的测试程序: 其后,可以得到如下结果: 二.使用Runtime.getRuntime()执行脚本文件

(转)java调用python脚本

这篇博客旨在吐血分享今天遇到的java调用python脚本遇到的坑,折腾了3个多小时终于可以跑通了,代码超级短,但网上的好多资料都是抄来抄去的,很少有能够直接跑通的,尤其是针对你的python文件中用到第三方类库的情况. 先来说说我为什么要用java调用python代码,原因就在于python在爬虫方面提供了丰富的类库,但我本来已经有一套java操作数据库以及消息中间件的代码,这些代码用python实现的话可能需要至少一周时间,为了减少时间成本,因此我决定用java调用python脚本的方式使用

matlib调用python时转py格式为matlib格式

因为需要,我用matlib调用python代码. 调用成功但是遇到问题 如下 调用完的结果为python格式   (py.list,py.xx) matlib根本不能用 查了半天一个能解决的方法都没 最后disp了一下这个数组 下面提升 使用single方法转为matlib格式数据 开心 pic=xxxxx; %xxxxx为py的输出格式 single(pic); 原文地址:https://www.cnblogs.com/bob-jianfeng/p/10989342.html