用python语言调用Go语言函数

2020-07-16 宋洋葱 宋洋葱

c语言几乎是所有高级语言的桥梁,通过c语言编译的动态链接库(.so文件),其他高级语言即可调用c语言实现的功能,以便提高性能。

一般情况下,高级语言都实现了对动态链接库的调用支持。

比如用golang实现一个aes加密解密的功能,提供给python语言或者其他语言调用。

因为c的数据类型和golang不一致,因此在方法的参数和返回值上就需要做一层转换。否则golang实现的方法,C语言无法识别参数和返回值的类型。golang提供C模块处理这样的对应关系,也提供cgo这样的工具转换golang代码。

假设现在有个需求是:用python语言调用Go语言函数。

实现这样的功能需要以下几个步骤:

  • 1、使用纯golang实现所需要的函数或功能
  • 2、导入C模块,使用c语言支持的参数类型和返回值类型,包装一层需要导出的函数。
  • 3、在上述需要导出的函数前面增加导出的注释//export yourfunc

举例,用golang实现一个字符串加密解密的功能,然后编译成静态链接库,供python调用。

1、go语言实现的加密解密:mycrypto.go

package main

func AESEncrypt(src string) (encrypted string) {
    // your code
	return encrypted
}

func AESDecrypt(encrypted string) (decrypted string) {
    //your code 
	return decrypted
}

func main(){
    //test
}

2、导入C模块,并包装需要导出的函数,并增加export encrypt注释

package main

import "C"

func AESEncrypt(src string) (encrypted string) {
    // your code
	return encrypted
}

func AESDecrypt(encrypted string) (decrypted string) {
    //your code 
	return decrypted
}

//export encrypt
func encrypt(endata *C.char) *C.char {
	ens := C.GoString(endata)
	encrypted := AESEncrypt(ens)
	return C.CString(encodedStr)
}

//export decypt
func decypt(dedata *C.char) *C.char{
	des := C.GoString(dedata)
	decrypted := AESDecrypt(des)
	return C.CString(decrypted)	
}

func main(){
    //test
}

3、编译

go build -buildmode=c-shared -o mycrypto.so mycrypto.go
mkdir -p ./lib/linux-amd64
mv mycrypto.so mycrypto.so.h ./lib/linux-amd64

此时会生成mycrypto.so、mycrypto.h文件。

不同计算机平台编译的.so文件不能在其他平台使用,如linux编译的.so不能在windows下使用。

4、python调用动态链接库

为了把c语言的各种数据类型转换成python支持的类型,需要用到ctypes

import ctypes

lib = ctypes.cdll.LoadLibrary("./lib/linux-amd64/mycrypto.so")

lib.encrypt.restype = ctypes.c_char_p
lib.encrypt.argtype = ctypes.c_char_p

lib.decypt.restype = ctypes.c_char_p
lib.decypt.argtype = ctypes.c_char_p

def encrypt(txt):
	res = lib.encrypt(txt.encode("utf-8"))
	return res.decode()

def decypt(code):
	res = lib.decypt(code.encode("utf-8"))
	return res.decode()

到此大功告成,其他语言如R、Java、php等也可调用.so,大家可自行搜索实现。

注意点:

  • 编写c语言支持的导出函数需要处理参数和返回值类型,必须是c支持的类型。
  • 其他语言调用的时候,如string类型必须序列化(如:python的str.encode()),不可直接调用。

参考文献: