如何在go中执行python3代码

2023-05-16

go-python3使用指南与踩坑记录

简介

​ 在开发工作中有时候会有需要和其他语言进行交互的需求,笔者前段时间就接到了需要在go中调用python3的需求,这种需求笔者想到了两种解法,一是直接在代码中调用python3,二是使用shell命令执行python3脚本。在本文中主要介绍了在go中使用go-python3这个库调用python3的一些实践历程与踩坑经历。关于使用脚本在另一篇文章:https://blog.csdn.net/LuciferMS/article/details/121888491

环境搭建

​ github上有现成的go调用python3开源框架,https://github.com/DataDog/go-python3,值得注意的是目前这个库只支持python3.7,笔者开始的时候电脑上装的是python3.8,所以在这个上面花费了好多时间,不过这也给了我一个教训,就是使用一个开源库的时候一定要好好看他的readme.md,留意一下有什么值得注意的地方。

在这里插入图片描述

​ 使用go get github.com/DataDog/go-python3就可以获取这个库到你的项目中,但是也有可能会报错,如果是pkg_config的问题可以在电脑上安装一下这个依赖,mac上安装:brew install pkg_config ,再试一次,问题就迎刃而解了。

程序demo

​ 笔者的程序目录结构是这样的:
在这里插入图片描述

​ 下面我们来看这个库的具体使用demo:

1.go代码 test_python3.go

package main

import (
   "fmt"
   "github.com/DataDog/go-python3"
   "log"
   "os"
)

func ImportModule(dir, name string) *python3.PyObject {
   sysModule := python3.PyImport_ImportModule("sys")
   path := sysModule.GetAttrString("path")
   pathStr, _ := pythonRepr(path)
   log.Println("before add path is " + pathStr)
   python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(""))
   python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(dir))
   pathStr, _ = pythonRepr(path)
   log.Println("after add path is " + pathStr)
   return python3.PyImport_ImportModule(name)
}

func main(){
   python3.Py_Initialize()
   if !python3.Py_IsInitialized() {
      fmt.Println("Error initializing the python interpreter")
      os.Exit(1)
   }
   path, _ := os.Getwd()
   helloPy := ImportModule(path + "/py3", "test_python3")
   if helloPy == nil {
      log.Fatalf("helloPy is nil")
      return
   }
   helloFunc := helloPy.GetAttrString("test_print_name")
   if helloFunc == nil {
      log.Fatalf("helloFunc is nil")
   }
   var args = python3.PyTuple_New(1)
   python3.PyTuple_SetItem(args, 0, python3.PyUnicode_FromString("python3"))
   helloPy3Str := helloFunc.Call(args, python3.Py_None)
   if helloPy3Str == nil {
      log.Fatalf("helloPy3Str is nil")
   }
   funcResultStr, _ := pythonRepr(helloPy3Str)
   log.Println("func result: " + funcResultStr)
}

func pythonRepr(o *python3.PyObject) (string, error) {
   if o == nil {
      return "", fmt.Errorf("object is nil")
   }

   s := o.Repr()
   if s == nil {
      python3.PyErr_Clear()
      return "", fmt.Errorf("failed to call Repr object method")
   }
   defer s.DecRef()

   return python3.PyUnicode_AsUTF8(s), nil
}

2.python代码test_python3.py

import os

from test_import_py import f
from test_import_py import x

# import cython
def test_print_name(name):
    print(os.getcwd())
    y = f(1.1)
    print(x)
    return y

3.python&cython代码

import cython

x = 3

@cython.cfunc
@cython.exceptval(-2, check=True)
def f(x: cython.double) -> cython.double:
    return x ** 2 - x

程序运行结果

在这里插入图片描述

程序测试点

  1. 如何引入执行一个python方法?
  2. 在python代码中引入别的模块会不会有问题
  3. 在python程序中写Cython会不会有问题

结果解析

  1. python3.Py_Initialize() 调用这个方法可以初始化python3的执行环境,只有执行了这个方法才可以运行相关的python3操作,值得注意的是这个方法既没有返回值,也不能重复初始化,重复初始化会报错。这个方法没有返回值意味着我们没法从这个方法中感知它的执行过程是否有问题,但是他也提供了python3.Py_IsInitialized() 这个方法来让我们知道python3的环境是否初始化完成。这两个方法要一起使用。

  2. 添加搜索路径 ,我们要往sys.path中添加我们写的python代码所在的目录作为搜索路径,添加进去之后,文件之中的代码才可以作为一个模块被import进来。值得注意的是,当我们import一个文件的时候,会把这个py脚本的代码执行一边,如py代码中的print("hello")就被执行了

  3. 执行代码,其中有一些api和值得关注的点如下:

    • PyImport_ImportModule:引入一个模块,通俗的理解就是传入一个文件名(前提是这个文件名所在的目录已经被引入到搜索路径下面,也就是sys.path下面),值得注意的是,如果这个被引入的文件路径不对或者不存在,这个方法返回的是nil,并不会报错,如果报错了,那应该是我们引入的文件中的python代码存在着问题,笔者遇到过一个坑,就是python代码中import的一些依赖并没有在代码中有使用,然后就报错了
    • GetAttrString:根据名字获取模块中的一个变量,返回的是一个pythonObject,比如我们再a.py中定义了一个demo = 1 那么这个时候我们a.GetAttrString(“demo”)就获取到了这个变量了,当然这里也可以获取一个方法。
    • Call:调用一个方法,值得注意的是这里的传参是一个tuple,如果不是tuple,Call方法会返回nil。还有一点值得关注的地方是,这里存在着一个并发的问题,如果一个函数在Call执行的过程中,再次被调用,此时python环境就会crash,感觉这个应该是python的原因,python的GIL机制,不能支持真正的并发运行代码。这个demo会在后面贴出。
    • pythonRepr,封装的代码,将一个python对象转化为字符串。

并发调用方法的坑

test_concurrency.go

package main

import (
   "fmt"
   "github.com/DataDog/go-python3"
   "log"
   "os"
)

func main()  {
   python3.Py_Initialize()
   if !python3.Py_IsInitialized() {
      fmt.Println("Error initializing the python interpreter")
      os.Exit(1)
   }
   sysModule := python3.PyImport_ImportModule("sys")
   path := sysModule.GetAttrString("path")
   //pathStr, _ := pythonRepr2(path)
   //log.Println("before add path is " + pathStr)
   python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(""))
   python3.PyList_Insert(path, 0, python3.PyUnicode_FromString("./py3"))
   concurrencyFile := python3.PyImport_ImportModule("test_concurrency")
   if concurrencyFile == nil {
      log.Fatalf("concurrency is nil")
      return
   }
   testFunc := concurrencyFile.GetAttrString("test_func")
   if testFunc == nil {
      log.Fatalf("testFunc is nil")
      return
   }
   go func() {
      testFunc.Call(python3.Py_None, python3.Py_None)
      for  {

      }
   }()
  	//time.Sleep(10 * time.Second)
   go func() {
      testFunc.Call(python3.Py_None, python3.Py_None)
      for  {

      }
   }()
   select {

   }
}

test_concurrency.py

import time

def test_func():
    print("hello")
    time.sleep(10)
  • 这个程序的大概意思就是一个python方法耗时是10s(执行的时候人为的sleep 10s),然后在go中有两个协程并发的去调用这个方法,这个程序的执行结果就是python crash,

  • 解决方法1: 但是如果将注释中的代码也加入其中,也就是说在两个协程之间加入11s的沉睡,这样两个协程对这个func的调用就是串行的了,这样就不会有问题。

  • 解决方法2:

    将go代码改成下面这个样子,改动点在代码中已标出

    package main
    
    import (
       "fmt"
       "github.com/DataDog/go-python3"
       "log"
       "os"
    )
    
    func main()  {
       python3.Py_Initialize()
       if !python3.Py_IsInitialized() {
          fmt.Println("Error initializing the python interpreter")
          os.Exit(1)
       }
       sysModule := python3.PyImport_ImportModule("sys")
       path := sysModule.GetAttrString("path")
       python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(""))
       python3.PyList_Insert(path, 0, python3.PyUnicode_FromString("./py3"))
       concurrencyFile := python3.PyImport_ImportModule("test_concurrency")
       if concurrencyFile == nil {
          log.Fatalf("concurrency is nil")
          return
       }
       testFunc := concurrencyFile.GetAttrString("test_func")
       if testFunc == nil {
          log.Fatalf("testFunc is nil")
          return
       }
       go func() {
          ensure := python3.PyGILState_Ensure() //获取python GIL(全局解释器)状态 0:释放 1:占用
          fmt.Println(ensure)
          testFunc.Call(python3.Py_None, python3.Py_None)
          for  {
    
          }
       }()
      go func() {
      	for {
      		if  python3.PyGILState_Ensure() == python3.PyGILState(0) {
      			testFunc.Call(python3.Py_None, python3.Py_None)
      			break
      		} else {
    
      		}
      	}
      	for  {
    
      	}
      }()
       select {
    
       }
    }
    

上面代码的改动主要是添加了对python GIL状态的判断,python由于又全局解释器的存在,所以不能并行执行多线程的代码,这个并发的问题应该也是由于GIL的存在才会导致的。笔者翻阅了Cython的API文档,找到了这个方法可以获取GIL的状态,这个问题才得以解决,但是感觉这也不是最优的方案,因为这样代码还是串行执行的,后面再探究一下有没有优化的空间吧。

总结

​ 其实现在回头看来,这段探索历程还是很有意思的,在工作中能有这样的体验当然也是相当棒的。
​ 话说回来,笔者也不是很懂python,所以一些东西都是摸着石头过河,很多结论都带有一丝猜测在里面,有不对支持还请读者多多提点。

参考

  • python GIL: https://www.cnblogs.com/traditional/p/13289905.html
  • Cython的Api文档: https://docs.python.org/zh-cn/3/c-api/index.html
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在go中执行python3代码 的相关文章

  • VB中实现延时(等待)的几种方法

    在程序流程中经常要延时一段时间后再继续往下执行 在VB中常用的有以下几种方法 因为Timer控件打乱了程序的流程所以一般不用它 1 使用Windows API函数Sleep 新建一个工程 添加一个TextBox控件和一个CommandBut
  • Ubuntu上CUDNN下载及安装过程详细介绍

    前言 显卡驱动以及CUDA安装好后 xff08 https blog csdn net avideointerfaces article details 100558457 xff09 xff0c 就可以根据cuda的版本以及平台类型 xf
  • 苹果开发者账号续费不成功?提示你的支付授权失败?看这里...

    苹果开发者账号续费失败 xff0c 提示 xff1a 你的支付授权失败 请核对你的信息并重试 或尝试其他支付方式 请联系你的银行了 公司开发者账号即将到期 xff0c 一年一度续费的重任又落到我肩膀上了 xff0c 那么我们就进入正题吧 一
  • WPF 开发 —— 几何图形(PathGeometry)

    参考资料 xff1a WPF 2D绘图 3 PathGeometry WPF 2D绘图 2 Geometry WPF 2D绘图 1 Shape WPF Geometry Transformation Tool
  • C++ 求三个数的乘积

    编程实现输入任意三个整数a b c xff0c 将这三个数的乘积输出 xff1b Input 三个整数 xff0c 每个数字空格隔开 分别表示a b c三个变量的值 xff08 1 lt a lt 100 1 lt b lt 1000 1
  • 中文Cookie导致Session丢失的问题

    在网络上 xff0c 已经有无数的文章讨论Session丢失问题 xff0c 其中的原因有非常多的种类 xff0c 在此不再赘述 下面我们描述一种特殊的Session丢失现象 xff1a 1 在绝大多数客户端上 xff0c Session都
  • Access.Image Decoder

    版本 xff1a V0 41 系统要求 xff1a Access 2000及以上版本 软件介绍 xff1a Access中的Image控件将所有图形数据保存在PictureData属性数组中 xff0c 通过修改PictureData xf
  • 直接粘贴剪贴版的位图数据到Image控件

    调用范例 xff1a Private Sub Command1 Click PasteToImage Me Image0 End Sub 模块段代码 xff1a Option Compare Database Option Explicit
  • Access.Image BMP图像高速切割函数

    本函数可以在你自己的程序中调用 xff0c 从已有BMP中切割任意一个矩形块 使用本函数 xff0c 即使你对BMP格式一窍不通 xff0c 也可以在自己的程序中自由地切割BMP xff08 比方说拼图游戏程序 xff09 附件包含 xff
  • 优化Microsoft Access提高速度

    压缩 压缩 要保证经常性的压缩你的程序代码 当你在开发和使用Microsoft Access数据库时 xff0c 你要经常性地增加和删除数据 代码等等 现在的问题是Microsoft Access并不能有效地释放已分配的但被删除的对象空间
  • LINUX Shell 下求两个文件交集和差集的办法

    假设两个文件FILE1和FILE2用集合A和B表示 xff0c FILE1内容如下 xff1a a b c e d a FILE2内容如下 xff1a c d a c 基本上有两个方法 xff0c 一个是comm命令 xff0c 一个是gr
  • 安装ensp 图文详解(超详细)

    文章目录 ensp的详细安装步骤 xff1a 分享安装包 xff08 在下载ensp之前需要安装好该三个应用 xff09 Virtualbox下载地址 xff1a https www virtualbox org wireshark下载 h
  • Microsoft Access秘密、技巧和陷阱

    摘要 这篇文章概括了当使用Microsoft Access 时 xff0c 通过使用Access Basic可以增加应用程序的执行速度 xff0c 减少编码量 xff0c 同时也减少在为Microsoft Windows应用程序接口编程时
  • 浅析Windows编程的剪贴板机制

    摘要 xff1a 本文对Windows剪贴板机制作了深入 全面的阐述 xff0c 具体内容包括 xff1a 文本 位图 DSP 自定义格式剪贴板的使用和多数据项和延迟提交技术 关键词 xff1a VC 43 43 6 0 xff1b 剪贴板
  • Matthew Curland的VB函数指针调用

    Matthew Curland简介 xff1a Visual Studio开发小组成员 xff0c 参与开发了VB的IntelliSense和Object Browser 他是VB资深专家 xff0c 对VB有非常深入的研究 xff0c 堪
  • Access中神秘的Criteria(上)

    看到这个标题 xff0c 很多读者可能会迷惑 xff0c 什么是Criteria xff1f 我用了这么久Access xff0c 怎么从来没听说国 我猜想很多读者都是怀着好奇的心情点击的这个主题 但是也许你不知道 xff0c Access
  • Access多条件格式研究笔记(上)

    之前见过论坛好几个帖子讨论过超过三个的条件格式 xff0c 当时因为没有用过条件格式 xff08 通常遇到类似需求我就直接用VBA写代码了 xff09 xff0c 所以也无法加入讨论 xff0c 但是开始留意条件这个这个功能 适逢周末有点空
  • Access 窗体控件事件集线器(EventsHub)V 1.1

    模块名称 xff1a EventsHub 模块版本 xff1a V 1 1 模块介绍 xff1a 很多时候我们在窗体中会有很多类似的控件 xff08 比方说40多个TextBox xff09 xff0c 对于这些类似的控件我们需要编制类似的
  • 窗体类中慎用Option Explicit

    今天遇到一个怪病 xff0c 困扰了我几个小时 xff0c 我有一段程序读取窗体上的切换按钮状态 xff0c 在一个新文件中运行得很好 xff0c 但是同样一个按钮 xff0c 同样代码 xff0c 贴到另外一个窗体 xff0c 却怎么也无
  • 窗体控件绑定数据库字段后的Value类型

    下面三张图展示了各种数据类型的字段绑定到窗体控件后的控件值类型 需要注意的是 xff0c 窗体类中是否使用Option Explicit xff0c 对于空值类型影响很大 xff0c 具体请看本人的另一篇文章 窗体类中慎用Option Ex

随机推荐

  • Dim 的陷阱

    代码一 xff1a Dim x 100 y 100 As Byte Call ReadBytes x Public Sub ReadBytes ByRef z As Byte 39 Do Nothing End Sub 对以上代码进行编译
  • 谨慎使用单精度/双精度数值类型

    前言 在近日几个帖子里面 xff0c 和QQ群的讨论里面 xff0c 我发现很多网友都遇到的问题都是因为不恰当地使用了单精度 双精度数值 因此想专门就这个话题谈一下 单精度和双精度数值类型最早出现在C语言中 xff08 比较通用的语言里面
  • Java的内存结构(Memory Structure)和垃圾收集(Garbage Collection)图解

    JVM 内存包含如下几个部分 xff1a Heap Memory 存放Java对象 Non Heap Memory 存放类加载信息和其它meta dataOther 存放JVM 自身代码等 在JVM启动时 xff0c 就已经保留了固定的内存
  • MD5加密的VB类模块源码

    在窗体中调用时如下 set c1 61 new class 39 class 为你存的类模快名 text1 text 61 md5 string calc sourcestring 类模块源码如下 Option Explicit 39 Vi
  • 四种整数数据类型的性能对比

    在我们写VBA程序的时候 xff0c 我们经常要面对数据类型定义的选择 xff0c 有的情况下 xff0c 业务本身对于数据类型有要求和限制 xff0c 那么我们并不难以选择 xff0c 有些时候却没有限制 xff0c 我们可以任意选用四种
  • VBA/VB获取硬盘序列号

    Private Declare Function GetVolumeInformation Lib 34 kernel32 dll 34 Alias 34 GetVolumeInformationA 34 ByVal lpRootPathN
  • .NET 4 on Windows7 / Mono 2.10.8.1 on Ubuntu 12.04 差异记录

    环境A xff1a NET 4 on Windows 7 环境B xff1a Mono 2 10 8 1 on Ubuntu 12 04 1 Socket Receive byte 当远程连接被断开时 xff1a A xff1a 立即返回0
  • 计算机视觉学习知识框架体系总结整理

    计算机视觉的三个基本任务 xff1a 1 xff09 对象检测 object detection 2 xff09 对象跟踪 object tracking 3 xff09 对象分割 object segmentation 一 xff1a F
  • VS Code 配置Java环境问题解决方式

    1 问题 1 1 改变插件的储存位置 1 2 Java环境变量配置 1 3 VS Code Java 工作区建立 1 4 code runner 运行输出为乱码 2 解决方法 2 1 储存位置更改 xff08 1 xff09 改变vs桌面快
  • YOLOv4-pytorch训练自己的数据集

    YOLOv4 pytorch训练自己的数据集 YOLOv4 pytorch介绍环境配置运行环境安装依赖包 准备工作Git clone YOLOv4准备数据集下载PascalVOC MSCOCO 2017数据集准备自己的数据集 下载权重文件
  • 【DEBUG日记】cannot import name ‘amp‘

    问题描述 xff1a 在使用WongKinYiu PyTorch YOLOv4进行训练时 xff0c 报错 xff1a Traceback span class token punctuation span most recent call
  • 【DUBUG日记】subprocess.CalledProcessError: Command ‘[‘ninja‘, ‘-v‘]‘ returned non-zero exit status 1.

    项目场景 xff1a 这是在编译FCOS 主要是其中基于maskrcnn benchmark部分 时报的错误 问题描述 xff1a 在执行 python setup py build develop no deps 时 xff0c 会Run
  • YOLOv5部署到Windows的C++(使用LibTorch)

    YOLOv5部署到Windows的C 43 43 xff08 使用LibTorch xff09 前言准备内容配置环境编译YOLOv5 检测可能遇到的问题 前言 由于项目需要 xff0c 我们要将YOLOv5部署到Windows上的C 43
  • 如何在Java中选择Map/List/Set

    很实用 xff0c 分享一下 简单版本 复杂版本 参考 xff1a http initbinder com articles cheat sheet for selecting maplistset in java html http ww
  • 一文搞定 Visual Studio 配置 OpenCV环境

    文章目录 前言准备工作系统环境变量配置VS项目环境配置检验补充 前言 在 Visual Studio 上配置 OpenCV 环境是极其恼人的事情 xff0c 尤其是对于初学者 xff0c 经常几个小时过去了都配不好 xff0c 将我们对代码
  • PyTorch 轻松节省显存的小技巧

    文章目录 前言一 大幅减少显存占用方法1 模型2 数据 二 小幅减少显存占用方法1 使用inplace2 加载 存储等能用CPU就绝不用GPU3 低精度计算4 torch no grad5 及时清理不用的变量6 分段计算 总结 前言 如今的
  • 【OpenCV经验总结】——视频篇

    文章目录 前言一 视频读写二 视频参数的获取和设置 前言 本篇汇总了本人在OpenCV使用过程中总结的关于视频处理的一些函数使用经验及Bug处理经验 xff0c 会根据后续使用情况逐步更新 一 视频读写 span class token k
  • 【OpenCV经验总结】——图片预处理篇

    文章目录 前言一 图片读写二 调整大小和填充三 滤波四 边缘提取五 腐蚀和膨胀 xff0c 开闭运算 前言 本篇汇总了本人在OpenCV使用过程中总结的关于图片的一些函数使用经验及Bug处理经验 xff0c 会根据后续使用情况逐步更新 一
  • 【DEBUG日记】torch.unique() 报错

    问题描述 在模型输出数据的后处理中 xff0c 在使用torch unique 时报以下错误 xff1a RuntimeError isDifferentiableType variable scalar type INTERNAL ASS
  • 如何在go中执行python3代码

    go python3使用指南与踩坑记录 简介 在开发工作中有时候会有需要和其他语言进行交互的需求 xff0c 笔者前段时间就接到了需要在go中调用python3的需求 xff0c 这种需求笔者想到了两种解法 xff0c 一是直接在代码中调用