python + go


目录
  • 1、Python代码
  • 2、Go代码
  • 3、编译为.so文件供Python调用。
  • 4、设置返回值
  • 5、python 调用GO传递 字符串参数
  • 6、Python,ctypes , c 类型对应表

1、Python代码

from ctypes import *
import time


if __name__ == '__main__':
    startTime = time.time()

    s = CDLL("s1.so")  # 加载s1.so文件
    result = s.run(100000000)  # 调用Go生成的.so文件里面的run函数
    print("result:", result)

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

2、Go代码

package main

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

//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
}

func main() {
  //main函数中什么都不要写,和包名main要对应
}

3、编译为.so文件供Python调用。

go build -buildmode=c-shared -o 输出的.so文件 go源文件

go build -buildmode=c-shared -o s1.so s1.go

4、设置返回值


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

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

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

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

5、python 调用GO传递 字符串参数

cron_set = '"30  59  */15 * * ?"'
chr = create_string_buffer(bytes(cron_set, encoding='utf8'))  # 路径改成相对路径
# Go 用 *C.char 接受

6、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 | char1 | 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 |

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

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拿到的会是什么呢?拿到会和原来的一样,一堆数字,这里就不举栗子了。

转自:https://mp.weixin.qq.com/s/9Q2CXpIdCu82OA92gYux8w