Go ,编译型语言,性能很好,原生高并发,跨平台,语法简单,有自动GC,相对安全的指针操作。其编译特性在目前各种代码/数据保密需求的环境下同样也成为一种优势。
Python ,解释性语言,语法简单,更加贴近英语的书写习惯,不过性能不好,但又因为他的基于C解释器,很容易去和C的库进行通讯,因此也被称为“胶水语言”。
实例代码
为了便于操作和理解,一下所有打包过程均基于如下示例代码(main.go
)进行处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
var ret int
/* 调用函数并返回最大值 */
ret = max(a, b)
fmt.Printf( "最大值是 : %d\n", ret )
}
/* 函数返回两个数的最大值 */
//export max
func max(num1, num2 int) int {
/* 定义局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
非常重要:,export 表示把go的函数映射到python的函数调用,如果没有export(格式必须是 //export func_name
),那么就不能生成.h文件,python也就无法调用该函数
同时有几个注意事项:
- //export add 在函数定义之前添加上注释来告诉编译器哪些定义可以被 C 引用,注意 // 和 export 之前不能有空格,否则会导出失败的。
- main() main 函数一定不能少,即使没有任何一行代码也没事;
import "C"
这个必须要加载 Go 源文件前,这一点必须做,应该就是告诉编译器我要即将编译的软件需要做为 C 的库而不直接是二进制。这个包也提供一些功能让 Go 去直接操作 C 的数据结构等等。
编译成可执行文件(直接运行)
go本身支持对代码进行打包,所以可以直接通过build 将代码打包可执行文件。1
2
3go build main.go
指定打包后文件名称,默认命名为go代码文件前缀
go build -o max main.go
跨平台交叉编译
和其他的编程语言不同,很多编程语言都是基于编译平台生成可执行的二进制文件,并不能跨平台生成可执行文件(比如没办法再windows下生成Linux可用的执行文件),但是再使用go进行编译的过程中发现,GO支持这个功能,可以实现windows进行代码开发,编译后直接再Linux上运行。
Mac 下编译 Linux 和 Windows 64位可执行程序
1 | 编译成Linux可用 |
Linux 下编译 Mac 和 Windows 64位可执行程序
1 | 编译成Mac可用 |
Windows 下编译 Mac 和 Linux 64位可执行程序
1 | 编译成Mac可用 |
GOOS:目标平台的操作系统(darwin、freebsd、linux、windows)
GOARCH:目标平台的体系架构(386、amd64、arm)
交叉编译不支持 CGO 所以要禁用它
编译成共享静态库(cython调用)
编译成c可以调用的库,在go代码的导入阶段必须执行 import "C"
。1
go build --buildmode=c-archive -o test.lib.a test.go
其中,–buildmode=c-archive 告诉 Go 来编译一个静态库,-o 是输出文件的名字,这里我们输出为 library.a。此时,目录下应该有一个 library.a 的文件和 library.h 的头文件。
打包成动态链接库(python调用)
1 | go build --buildmode=c-shared -o library.so test.go |
打包完成后会生成有两个文件 library.so 和 library.h 。
然后我们就可以在python代码中调用相关的功能了1
2
3
4
5
6
7
8
9
10
11
12
13
14import ctypes
lib = ctypes.cdll.LoadLibrary("library.so") # 对应生成的动态链接库代码
# 简单的调用
print(lib.max(1, 2)) # max就是对应打包代码中 export的函数
# 系统的调用,需要对函数的输入和输出的数据类型进行声明
GoInt64 = ctypes.c_int64
GoInt = GoInt64
add = lib.add
add.argtypes = [GoInt64, GoInt64]
add.restype = GoInt64
res = add(GoInt(1), GoInt(2))
在整体使用的时候,需要注意相关变量的定义,这部分后续有具体业务使用时,在进行细化补充。
其中各类数值类型在不同语言环境下的对应关系如下,
| ctypes | Python | C | Go.h | Go |
| ———— | ————– | ————————————– | —————- | ——————— |
| c_bool | bool | _Bool | bool | bool |
| c_byte | int | char | GoInt8 | int8 |
| c_ubyte | unsigned char | int | GoUint8 | uint8 |
| c_short | short | int | GoInt16 | int16 |
| c_ushort | unsigned short | int | GoUint16 | uint16 |
| c_int | int | int | GoInt32 | int32 |
| c_uint | int | unsigned int | GoUint32 | uint32 |
| c_ulong | int | unsigned long | GoUint32 | uint32 |
| c_longlong | int | int64 or long long | GoInt64 or GoInt | int64 or int |
| c_ulonglong | int | unsigned int64 or unsigned long long | GoUint64 | uint64 |
| c_size_t | int | size_t SIZE_TYPE | GoUintptr | uintptr |
| c_ssize_t | int | ssize_t or Py_ssize_t | Go中无定义 | |
| c_float | float | float | GoFloat32 | float32 |
| c_double | float | double | GoFloat64 | float64 |
| c_longdouble | float | long double | GoFloat64 | float64 |
| 无定义 | float | float _Complex | GoComplex64 | complex64 |
| 无定义 | float | double _Complex | GoComplex128 | complex128 or complex |
其中各类字符串类型在不同语言环境下的对应关系如下,
| ctypes | Python | C |
| ——— | ——————– | ————————- |
| c_char | 长度为1的bytes | char |
| c_wchar | 长度为1的string | wchar_t |
| c_char_p | bytes object or None | char (NUL terminated) |
| c_wchar_p | string or None | wchar_t (NUL terminated) |
| c_char | 长度为1的bytes | char |
除了上述内容,还需要注意一些结构体的对应、指针、切片、通道的处理等问题,后续可以更详细的研究后述的相关参考资料。