c++调用python_实战 | 如何结合 Golang,提高 Python 的计算性能(下)

点击上方“AirPython”,选择“加为星标”

第一时间关注 Python 技术干货!

2aec15179bfcbbe5bf494adc786750ff.png

1. 前言

在上一篇文章中,我们成功的通过 Python 调用 Go 代码,并且把计算密集的交给了 Go 来进行计算

虽然计算的很快,但是在获取返回值时,似乎有点让人诧异,并不是我们想象的那样子

229d6de6a369863cfe05caa224c8d608.png

2. 返回值错误原因?

在上一篇文章中,我们似乎都忘了一张图

fa2efbb4c9530634e35b46a2f89742a5.png

.go 文件是 Go 的,不多说,.so 是 Python 调用 Go 的文件,那 .h 文件是什么?

似乎很没有存在感,我们的问题出,也就出在这个 .h 文件中,我们打开看一下

b1c046f7a27cdc886924784d02ee0616.png

我们往下翻翻,会找到 extern 开头的声明,这个意思是将 Go 代码转为 C 代码一一对应的参数,可见,转为 C 的是 GoInt 类型,这个类型具体什么意思呢???再找找看

70582c18855bb9af23db8e0b9aef5d85.png

在上面其实大概可以发现,GoInt 其实就是 GoInt64,GoInt64 的类型是 long long 类型,这是玩意???

其实我们在 Python 中调用 .so 文件使用的是 ctypes 模块,这个是有一个对应表的

773f5eaa9b2f46b1c4235e7e96696280.png

3. 查看对应表

对应表可以参考官网地址:

https://docs.python.org/3.5/library/ctypes.html

在 C 中的 long long 类型对应的 ctype 类型是 c_longlong

a08f8a4ee54d35df728830a7ad576fdf.png

所以我们需要在 Python 将 .so 中的返回值改一下,不能使用系统默认的了。

代码如下:

from ctypes import *
import time

class StructPointer(Structure):
    # 根据查表,C中的long long,对应的ctypes 是 c_longlong
    # _fields_必须是[](列表)类型,里面写对应的类型,固定格式
    _fields_ = [("p", c_longlong,),]

if __name__ == '__main__':
    beginTime = time.time()
    s = CDLL("s1.so")  # 加载s1.so文件
    s.run.restype = StructPointer # 声明.so的run函数返回值类型,固定格式
    result = s.run(100000000)  # 调用Go生成的.so文件里面的run函数

    print("result:", result.p)# 此处需要调用.p来获取值,和 _fields_对应
    endTime = time.time()
    print("耗时:", endTime - beginTime)

 再次执行,结果如下:

02b32bc9e79490e66266fc399a54f2f0.png

可以看到,这次 Python 执行的结果和 Go 执行结果就一个样了。

继续,换个数字试试

7c297b01d9e977805e81fc4330d744e0.png

基本可以确定,这次是没问题了

4. 如果是字符串,怎么办

Go 代码

package main

import (
  "C" //C必须导入
  "fmt"
)

//export run
func run(n int) int{
  /*
    必须要export 函数名
    //是注释的意思,相当于Python中的 #
    我也是第一次见注释还有作用
  */
  sum := 0
  for i := 0; i     sum += i
  }
  fmt.Println("我是Go代码,我跑完了,我的结果是:",sum)
  return sum
}

//export speak
func speak(n int) string{

  return "OMG 996好累呀,难得休息一天,好好休息"
}
func main() {
  //main函数中什么都不要写,和包名main要对应
}

下面一起来理解 .h 文件。编译之后打开 .h 文件如下图所示:

ce64d6d61e2f724b518286acefa93c40.png

可以看到,在 extern 的函数成了两个,但是他的返回值是 GoString,继续找

2054c5fd979c14fe96ed59ef3cb7fe9a.png

可以发现,其实 GoString 就是 _GoString_,继续找。

d23a12e986fa04fd81bd1fcffa7ce0ec.png

这次是一个结构体,里面其实是两个值,不在是单独的 long long 了,那 Python 中的继承类也要改一下了

5c3896485a4251fec65cd074fcbfc3b2.png

下面基本同上

5a5b52bbc22cdb09dde831ed177ff6f0.png

完整代码如下:

from ctypes import *
import time

class StructPointer(Structure):
    # 根据查表,C中的long long,对应的ctypes 是 c_longlong
    # _fields_必须是[](列表)类型,里面写对应的类型,固定格式
    _fields_ = [("p", c_longlong,),]

class StrPointer(Structure):
    # typedef struct { const char *p; ptrdiff_t n; } _GoString_;
    # ptrdiff_t == long long
    _fields_ = [("p", c_char_p), ("n", c_longlong)]

if __name__ == '__main__':
    beginTime = time.time()
    s = CDLL("s1.so")  # 加载s1.so文件

    s.run.restype = StructPointer # 声明.so的run函数返回值类型,固定格式
    result = s.run(100000798)  # 调用Go生成的.so文件里面的run函数
    print("result:", result.p)# 此处需要调用.p来获取值,和 _fields_对应

    s.speak.restype = StrPointer
    speakStr = s.speak()
    # 返回的是字节类型,需要转字符串,返回的内容在.p中,.n是切的长度,后面会跟一些介绍,不需要
    speakStr = str(speakStr.p[:speakStr.n], encoding="utf-8")
    print("speak:",speakStr)

    endTime = time.time()
    print("耗时:", endTime - beginTime)

运行后的结果如下:

e50519cb97725758a2b61cdde43d96ae.png

可以看到,调用 Go 代码成功的拿到了正确的字符串返回值

如果没有 .restype = StrPointer 拿到的会是什么呢?

拿到会和原来的一样,一堆数字,这里就不举例子了

5. 最后

至此,基本上在 Python 调用 Go 代码上的大坑都解决了,最复杂的是返回字符串类型,查了很多相关资料才解决。

关于返回其他类型的 Python 的 class 怎么写,我相信已经难为不到你们了,最复杂的都解决了,最简单的还不会吗?(除字符串类型以外其他 _fields_ 都是一个字段的)

我相信各位小伙伴学习能力还是很强的,人生苦短,Python 当歌,加油,奥利给!

如果你觉得文章还不错,请大家 点赞、分享、留言下,因为这将是我持续输出更多优质文章的最强动力!

推荐阅读实战 | 如何结合 Golang,提高 Python 的计算性能(上)聊聊 Python 面试最常被问到的几种设计模式(上)自动化篇 | 这些自动化场景,批处理脚本完全可以取代 Python! b95dcb04dfe8c9f843b36482c372b825.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值