如何利用Golang为Python编写模块

前言

​ 由于公司的Python项目中有关于支付签名与验签的模块,是自定的一些内部逻辑,基于安全性考虑, 希望改用C/C++或者Go 来重构该部分模块,做到加解签过程透明,上层代码只需要关心结果. 由于最近开始了Golang的学习,就尝试完成这部分工作,整个过程都是边踩坑边完成,下面以样例代码来分享一下整个过程的思路.

记录

​ Go里面需要显示的引入C模块, 让编译器支持生成动态链接库, 并且在代码中可以使用C语言的数据类型,这个至关重要. Calling Go code from Python code 摘取一个最简单例子

//libadd.go
package main

import "C"

//export add
func add(left, right int) int {
    return left + right
}

func main() {
}
go build -buildmode=c-shared -o libadd.so libadd.go
from ctypes import cdll
lib = cdll.LoadLibrary('./libadd.so')
print("Loaded go generated SO library")
result = lib.add(2, 3)
print(result)

The cgo export command is documented in go doc cgo, section "C references to Go". Essentially, write //export FUNCNAME before the function definition

有这么一段话, 需要显式注释//export add 把 add函数公开给C调用

本以为很简单的就能用, 兴致满满地把例子改一下, 改为简单的处理字符串的时候, 却发现跑不起来了.

//libadd.go
package main

import "C"

//export add
func add(left, right string) string {
    return left + right
}

func main() {
}
from ctypes import CDLL
lib = CDLL('./libadd.so')
print("Loaded go generated SO library")
result = lib.add("Hello", "World")
print(result)
  • 这时候运行是出错的

再次翻看资料发现这么一句话:

The python code is really short and this is only passing an integer back and forth (more complex string and struct cases are much more challenging).

这说明处理字符串的时候并不是简单改成string类型就可以.这时候翻开了BUILDING PYTHON MODULES WITH GO 1.5 , 这时能找到的最全面的资料, 可惜里面的过程都过于复杂, 整个思路是用Go去写C code, 类似写解释器一样, 去抽象出PyObject然后按照API标准来注册、处理、返回.我仅是希望以动态链接库 的方式来能调用就可以了.

我开始思考, 为何例子中使用int类型就可以, 我改成一个简单的接收string 返回string 却一直失败. py是利用ctypes来跟so模块进行交互, 这里存在一个代码的翻译过程 Py -> C -> Go, 我能想到的对于字符串数据类型的处理不一样原因引起(后面事实证明了我的猜想).那么思考一下, Py中的字符串传递到Go里面去使用什么类型来接收呢? 翻阅了大量资料, 所有答案在Python Doc 官网关于ctypes模块中有能找到.我们来看一下这图:

001.png

这里可以很清楚的看到Python3 ctypes中字符串 bytesstring 是对应的两种指针类型.同时提供了argtypesrestype 来显式转换动态链接库中函数的参数和返回类型.(参考StackOverFlow)

这时候按照思考的流程来修改代码

//libadd.go
package main

import "C"

//export add
func add(left, right *C.char) *C.char {
    // bytes对应ctypes的c_char_p类型,翻译成C类型就是 char *指针
    merge := C.GoString(left) + C.GoString(right)
    return C.CString(merge)
}

func main() {}

重新编译

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

Python中引用

import ctypes
add = ctypes.CDLL('./libadd.so').add
# 显式声明参数和返回的期望类型
add.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
add.restype = ctypes.c_char_p
left = b"Hello"
right = b"World"
print(add(left, right))

正确输出结果:

b"HelloWorld"

就这样, 一个基本的模块就完成, 只要关注传入参数和返回结果的数据类型处理, 我只需要丰富函数的处理逻辑,Go模块中函数内部实现对于Python是透明,只要参数正确即可.其中关于 cgo更多的信息, 大家可以自行查阅Golang.org

总结

  1. Python与Go之间的参数传递, 处理非INT型时需要都转为对应的C类型
  2. ctypes需要显式地声明DLL函数的参数和返回期望的数据类型
  3. 注意在Python3中字符串bytes和string的区别
  4. Go模块需要//export 声明外部可调用
  5. Go处理C的类型是需要显式转换
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,097评论 18 139
  • http://python.jobbole.com/85231/ 关于专业技能写完项目接着写写一名3年工作经验的J...
    燕京博士阅读 7,484评论 1 118
  • 个人笔记,方便自己查阅使用 Py.LangSpec.Contents Refs Built-in Closure ...
    freenik阅读 67,641评论 0 5
  • 前言 Python的创始人为Guido van Rossum。1989年圣诞节期间,在阿姆斯特丹,Guido为了打...
    依依玖玥阅读 3,537评论 6 37
  • 两本不错的书: 《Python参考手册》:对Python各个标准模块,特性介绍的比较详细。 《Python核心编程...
    静熙老师哈哈哈阅读 3,329评论 0 80