专栏首页Python爬虫与数据挖掘【进阶篇】Python+Go——带大家一起另寻途径提高计算性能

【进阶篇】Python+Go——带大家一起另寻途径提高计算性能

/1 前言/

在上一篇文章中,【基础篇】Python+Go——带大家一起另寻途径提高计算性能,我们成功的通过Python调用Go代码,并且把计算密集的交给了Go来进行计算,虽然计算的很快,但是在获取返回值时,似乎有点让人诧异,并不是我们想象的那样子。

/2 造成返回值错误的原因是什么?/

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

.go文件是Go的,不多说,.so是Python调用Go的文件,那.h文件是什么???似乎很没有存在感,我们的问题出,也就出在这个.h文件中,我们打开看一下

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

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

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

/3 Python,ctypes , c 类型对应表/

官网地址:

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

ctypes type

C type

Python type

c_bool

_Bool

bool (1)

c_char

char

1-character bytes object

c_wchar

wchar_t

1-character string

c_byte

char

int

c_ubyte

unsigned char

int

c_short

short

int

c_ushort

unsigned short

int

c_int

int

int

c_uint

unsigned int

int

c_long

long

int

c_ulong

unsigned long

int

c_longlong

__int64 or long long

int

c_ulonglong

unsigned __int64 or unsigned long long

int

c_size_t

size_t

int

c_ssize_t

ssize_t or Py_ssize_t

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char * (NUL terminated)

bytes object or None

c_wchar_p

wchar_t * (NUL terminated)

string or None

c_void_p

void *

int or None

根据上述表格我们可以发现,在C中的long long类型对应的ctype类型是c_longlong。

所以我们需要在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)

再次执行:

可以看到,这次Python执行的结果和Go执行结果就一个样了。继续,换个数字试试看。

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

/4 如果返回的是字符串呢?/

Go代码

package main

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

//export run
func run(n int) int{
  /*
    必须要export 函数名
    //是注释的意思,相当于Python中的 #
    我也是第一次见注释还有作用
  */
  sum := 0
  for i := 0; i < n; 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文件如下图所示:

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

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

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

下面基本同上。

完整代码如下:

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)

结果:

可以看到,调用Go代码成功的拿到了正确的字符串返回值,如果没有 .restype = StrPointer拿到的会是什么呢?拿到会和原来的一样,一堆数字,这里就不举栗子了。

/5 小结/

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

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

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

本文分享自微信公众号 - Python爬虫与数据挖掘(crawler_python),作者:Python进阶者

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-07

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【基础篇】Python+Go——带大家一起另寻途径提高计算性能

    Hello各位小伙伴你们好,我们都知道Python是一个生产力很高的语言,小编本人也非常喜欢Python,经常使用Python帮助小编以最高的效率完成最...

    Python进阶者
  • Python中的基础数据类型(List,Tuple,Dict)及其常用用法简析

    上次咱们简单的认识了String,Number,这次简单聊聊剩下的几个数据类型(列表,元组,字典)...

    Python进阶者
  • 在Ubuntu14.04中配置mysql远程连接教程

    上一篇文章,小编带大家学会了在Ubuntu14.04中安装MySQL,没有来得及上课的小伙伴们可以戳这篇文章:如何在Ubuntu14.04中安装mysql,今天...

    Python进阶者
  • 史上最全关于sorted函数的10条总结(文末附送书中奖名单)

    sorted 用于对集合进行排序(这里说的集合是对可迭代对象的一个统称,他们可以是列表、字典、set、甚至是字符串),它的功能非常强大,本文将深入浅出地介绍 s...

    sergiojune
  • Python 中排序方法的十条用法总结

    sorted 用于对集合进行排序(这里说的集合是对可迭代对象的一个统称,他们可以是列表、字典、set、甚至是字符串),它的功能非常强大,本文将深入浅出地介绍 s...

    崔庆才
  • Codeforces Round #665 (Div. 2)

    B题贪心构造,尽量别想太复杂,要不很容易绕不出来,可以分类讨论一下或者自己构造几个数组找找规律。

    ACM算法日常
  • 缓存系列文章--热点key问题

    我们通常使用 缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新,一般情况下这种模式已经基本满足要求了。   但是有两个...

    小程故事多
  • 动手实现 LRU 算法,以及 Caffeine 和 Redis 中的缓存淘汰策略

    那天我在 LeetCode 上刷到一道 LRU 缓存机制的问题,第 146 题,难度为中等,题目如下。

    古时的风筝
  • deno深入揭秘及未来展望

    node.js之父Ryan Dahl在一个月前发起了名为deno的项目,项目的初衷是打造一个基于v8引擎的安全的TypeScript运行时,同时实现HTML5的...

    前端博客 : alili.tech
  • deno深入揭秘及未来展望

    node.js之父Ryan Dahl在一个月前发起了名为deno的项目,项目的初衷是打造一个基于v8引擎的安全的TypeScript运行时,同时实现HTML5...

    欲休

扫码关注云+社区

领取腾讯云代金券