从python 迁移到GO

转载 2013年12月04日 14:06:49

这是一篇(长)博文, 介绍了我们在 Repustate 迁移大量 Python/Cython 代码到 Go 语言的经验。如果你想了解整个故事,背景和所有的事情,请继续往下读。如果你只是想了解 Python 开发者在一头扎进 Go 语言前需要了解什么,请点击一下链接:

从Python迁移到Go的建议(Tips & Tricks)

 

背景

在 Repustate,我们完成过的最棒的技术成就之一是实现了阿拉伯语的情感分析。阿拉伯语是一块难啃的硬骨头,因为它的词形变化相当复杂。比起譬如英语,阿拉伯语的分词(将一个句子切分呈几个独立的单词)也更困难,因为阿拉伯语的单词本身还可能会包含空白字符(例如:“阿列夫”在一个单词里的位置)。这也谈不上是泄密,Repustate 使用支持向量机(SVM)来获取一个句子背后最有可能的含义,并在其中加上情感元素。 总体上来说,我们使用了 22 种模型(22 个 SVM) 并且在一篇文档中,每一个单词我们都会加以分析。因此如果你有一篇 500 字的文档,那么基于 SVM,会进行十万次的比较。

 

Python

Repustate 几乎完全就是一个 Python 商店。我们使用 Django 来实现 API 和网站。因此(目前)为了保持代码一致,同时使用 Python 来实现阿拉伯语情感引擎是合情合理的。只是做原型和实现的话,Python 是很好的选择。它的表达能力很强悍,第三方类库等等也很好。如果你就是为了Web服务,Python 很完美。但是当你进行低级别的计算,大量依赖于哈希表(Python 里的字典类型)做比较的时候,一切都变慢了。我们每秒能处理大约两到三个阿拉伯文档,但是这太慢了。比较下来,我们的英语情感引擎每秒能处理大约五百份文档。

 

瓶颈

因此我们开启了 Python 分析器,开始调查是什么地方用了那么长时间。还记得我前面说过我们有 22 个 SVM 并且每个单词都需要经过处理吗?好吧,这些都是线性处理的,非并行处理。所以我们的第一反应是把线性处理改成 map/reduce 那样的操作。简单来说:Python 不太适合用作 map/reduce。当你需要并发的时候,Python 算上好用。在 2013 Python 大会上(译者:PyCon 2013),Guido 谈到了 Tulip,他的这个新项目正在弥补 Python 这方面的不足,不过得过段一段时间才能推出,但是如果已经有了更好用的东西,我们为什么还要等呢?

 

选 Go 语言,还是回家算了?

我在Mozilla的朋友告诉我,Mozilla 内部正在将他们大量的基础日志架构切换到 Go 语言上,部分原因是因为强大的 [goroutines]。Go 语言是 Google 的人设计的,并且在设计之初就把支持并发作为第一要务,而不是像 Python 的各种解决方案那样是事后才加上去的。因此我们开始着手把 Python 换成 Go 语言。

虽然 Go 代码还不算正式上线的产品,但是结果非常令人鼓舞。我们现在能做到每秒处理一千份文档,使用更少的内存,还不用调试你在 Python 里遇到:丑陋的多进程/gevent/“为什么 Control-C 杀不了进程”这些问题。

 

为什么我们喜欢 Go 语言

任何人,对编程语言是如何工作(解释型 vs 编译型, 动态语言 vs 静态语言)有一点理解的话,会说,“切,当然 Go 语言会更快”。是的,我们也可以用 Java 把所有的东西重写一遍,也能看到类似更快的改善,但那不是 Go 语言胜出的原因。你用 Go 写的代码好像就是对的。我搞不清楚到底是怎么回事,但是一旦代码被编译了(编译速度很快),你就会觉得这代码能工作(不只是跑起来不会错,而且甚至逻辑上也是对的)。我知道,这听上去不太靠谱,但是确实如此。这和 Python 在冗余(或非冗余)方面非常类似,它把函数作为第一目标,因此函数编程会很容易想明白。而且当然,go 线程和通道让你的生活更容易,你可以得到静态类型带来的性能大提升,还能更精细的控制内存分配,而你却不必为此在语言表达力上付出太多的代价。

 

希望能早点知道的事情(Tips & Tricks)

除去所有这些赞美之词以后,有时你真的需要在处理 Go 代码的时候,相对于 Python,改变一下思维方式。因此这是我在迁移代码时记录的笔记清单 —— 只是在我把 Python 代码转换到 Go 时从我脑子里随机冒出来的点子:

  • 没有内建的集合类型(必须使用map,并检查是否存在)
  • 因为没有集合,必须自己写交集,并集之类的方法
  • 没有 tuples 类型,必须写你自己的结构,或者使用 slices (即数组)
  • 没有类似 \__getattr__() 的方法,你必须总是检查存在性,而不是设置默认值,例如,在 Python 里,你可以这样写 value = dict.get(“a_key”, “default_value”)
  • 必须总是检查错误(或者显式的忽略错误)
  • 不能有变量/包没被使用,因此简单的测试也需要有时注掉一些代码
  • 在 [] byte 和 string 之间转换。 regexp 使用 [] byte (不可变)。这是对的,但是老把一些变量转换来转换去很烦人
  • Python 更宽松。你可以使用超出范围的索引在字符串里取一个片段,而且不会出错。你还可以用负数取出片段,但是 Go 不行
  • 你不能混合数据结构类型。也许这样也不太干净,但是有时在 Python 里,我会使用值是混合了字符串和列表的字典。但是 Go 不行,你不得不清理干净你的数据结构或者使用自定义的结构
  • 不能解包一个 tuple 或者 list 到几个不同的变量(例如:x, y, z = [1, 2, 3])
  • 驼峰式命名风格(如果你没有首字大写方法名/结构名,他们不会被暴露给其它的包)。我更喜欢 Python 的小写字母加下划线命名风格。
  • 必须显式检查是否有错误 != nil, 不像在 Python 里,许多类型可以像 bool 那样检查 (0, “”, None 都可以被解释成 “非” 集合)
  • 文档在一些模块上太散乱了,例如(crypto/md5),但是 IRC 上的 go-nuts 很好用,提供了巨大的帮助。
  • 从数字到字符串的转换(int64 -> string) 和 []byte -> string (只要使用 string([]byte))不太一样。需要使用 strconv。
  • 阅读 Go 代码比起 Python 那样写起来如伪代码的语言更像一门编程语言, Go 有更多的非字母数字字符,并且使用 || 和 &&, 而不是 “or”和“and”
  • 写一个文件的话,有 File.Write([]byte) 和 File.WriteString(string), 这点和 Python 开发者的 Python 之道:“解决问题就一种方法 ”相违背。
  • 修改字符串很困难,必须经常重排 fmt.Sprintf
  • 没有构造函数,因此惯用法是创建 NewType() 方法来返回你要的结构
  • Else (或者 else if)必须正确格式化,else 得和 if 配对的大括号在同一行。奇怪。
  • 赋值运算符取决于在函数内还是函数外,例如,= 和 :=
  • 如果我只想要“键”或者只想要 “值”,譬如: dict.keys() 或者 dict.values(),或者一个 tuples 的列表,例如:dict.items(),在 Go 语言里没有等价的东西,你只能自己枚举 map 来构造你的列表类型
  • 我有时使用一种习惯用法:构造一个值是函数的字典类型,我想通过给定的键值调用这些函数,你在 Go 里可以做到,但是所有的函数必须接受,返回相同的东西,例如:相同的方法签名
  • 如果你使用 JSON 并且 你的 JSON 是一个复合类型,恭喜你。 你必须构造自定义的结构匹配 JSON 块里的格式,然后把原始 JSON 解析到你自定义结构的实例中去。比起 Python 世界里 object = json.loads(json_blob) 要做更多的工作

 

是不是值得?

值得,一百万倍的值得。速度的提升太多了,以致很难舍弃。同时,我认为, Go 是目前趋势所在,因此在招新员工的时候,我认为把 Go 当作 Repustate 技术积累的重要一环会很有帮助。

相关文章推荐

Python 开发者在迁移到 Go(lang) 时需要知道哪些事?

本文最早由 Repustate 发布,主要介绍将代码迁移至 Go(lang) 时的注意事项。文章系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正文。

5个理由让你选择Go,抛弃Python

Python和Go你会选择那个?如果对Go了解较深的小伙伴肯定会先Go,如果不深肯定会选Python,那么接下来我们来讨论一下,选择Python的小伙伴,5个理由就会让你选择Go,我们一起看文章吧! ...

4种编程语言基本数据类型及其取值范围整理(C++,Java,Python,Go)

1 C++        C++定义的基本类型包括算术类型和void类型。算术类型分为整型(包括字符和布尔型)和浮点型,如下表所示: C++11新增了char16_t,char32_t(最小分别为1...

Cloudera Manager HBase Thrift 接口 Go/Python客户端

通过Thrift访问HBase数据库,Go/Python客户端。记录详细配置、调试、除错、调试过程。

web编程速度大比拼(nodejs go python)(非专业对比)

C10K问题的解决,涌现出一大批新框架,或者新语言,那么问题来了:到底谁最快呢?非专业程序猿来个非专业对比。 比较程序:输出Hello World! 测试程序:siege –c 100 –r 10...

Grumpy: Go 运行 Python

概览 Grumpy是一个把python代码编译成Go代码的编译器和一个运行时,它旨在替换CPython2.7. 关键的区别是它将Python源代码编译为Go源代码, 然后将其编译为本机代码,而不是字...

Python、Java、Scala、Go Package对照表

格式转换类 分类 Python Java Scala Go Excel xlwt/xlrd, XlsxWriter POI - - PDF PyPDF2 ...

以太坊连载(十):以太坊GO、JAVA、PYTHON、RUBY、JS客户端介绍

go-ethereum go-ethereum客户端通常被称为geth,它是个命令行界面,执行在Go上实现的完整以太坊节点。通过安装和运行geth,可以参与到以太坊前台实时网络并进行以下操作: ...

Python/Ruby/Go/Node 之四国大战

偶然的机会看到一篇 blog,文中详细的介绍了 Flask(Python), Sinatra(Ruby) 以及 Matini(Golang) 这三类微型框架的用法,并提供了各个框架在 Docker 下...

Docker容器热迁移研究历程chapter1:go语言学习

由于Docker项目是由go语言编写,根据我前一段时间的研究思路的整理,我大致划分来一下我的研究思路。           我研究了Docker的架构模式如下:           研究了该架构后我...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)