放弃Python转向Go语言:这9大理由就够了 !(附代码)

2017 年 10 月 21 日 数据派THU

来源:机器之

作者:Thierry Schellenbach

本文长度为3216字,建议阅读6分钟

本文为你介绍八个短时间可以完成的趣味机器学习项目。


转用一门新语言通常是一项大决策,尤其是当你的团队成员中只有一个使用过它时。今年Stream团队的主要编程语言从Python转向了Go。本文解释了其背后的九大原因以及如何做好这一转换。


为什么使用 Go


原因 1:性能

Go极其地快。其性能与Java或C++相似。在我们的使用中,Go一般比Pytho要快30倍。以下是Go与Java之间的基准比较:



原因 2:语言性能很重要


对很多应用来说,编程语言只是简单充当了其与数据集之间的胶水。语言本身的性能常常无关轻重。


但是Stream是一个API提供商,服务于世界500强以及超过2亿的终端用户。数年来我们已经优化了Cassandra、PostgreSQL、Redis等等,然而最终抵达了所使用语言的极限。


Python非常棒,但是其在序列化/去序列化、排序和聚合中表现欠佳。我们经常会遇到这样的问题:Cassandra用时1ms检索了数据,Python却需要10ms将其转化成对象。


原因 3:开发者效率&不要过于创新


看一下绝佳的入门教程《开始学习 Go 语言》(http://howistart.org/posts/go/1/)中的一小段代码:


  
    
    
    
  1. package main

  2. type openWeatherMap struct{}func (w openWeatherMap) temperature(city string) (float64, error) {

  3.    resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city)

  4.    if err != nil {

  5.        return 0, err

  6.    }

  7.    defer resp.Body.Close()

  8.    var d struct {

  9.        Main struct {

  10.            Kelvin float64 `json:"temp"`

  11.        } `json:"main"`

  12.    }

  13.    if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {

  14.        return 0, err

  15.    }

  16.    log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin)

  17.    return d.Main.Kelvin, nil}



如果你是一个新手,看到这段代码你并不会感到吃惊。它展示了多种赋值、数据结构、指针、格式化以及内置的HTTP库。


当我第一次编程时,我很喜欢使用Python的高阶功能。Python允许你创造性地使用正在编写的代码,比如,你可以:


  • 在代码初始化时,使用MetaClasses自行注册类别

  • 置换真假

  • 添加函数到内置函数列表中

  • 通过奇妙的方法重载运算符


毋庸置疑这些代码很有趣,但也使得在读取其他人的工作时,代码变得难以理解。


Go强迫你坚持打牢基础,这也就为读取任意代码带来了便利,并能很快搞明白当下发生的事情。


注意:当然如何容易还是要取决于你的使用案例。如果你要创建一个基本的 CRUD API,我还是建议你使用Django + DRF,或者Rails。


原因 4:并发性&通道


Go作为一门语言致力于使事情简单化。它并未引入很多新概念,而是聚焦于打造一门简单的语言,它使用起来异常快速并且简单。其唯一的创新之处是goroutines和通道。Goroutines是Go面向线程的轻量级方法,而通道是goroutines之间通信的优先方式。


创建Goroutines的成本很低,只需几千个字节的额外内存,正由于此,才使得同时运行数百个甚至数千个goroutines成为可能。你可以借助通道实现 goroutines 之间的通信。Go运行时间可以表示所有的复杂性。Goroutines以及基于通道的并发性方法使其非常容易使用所有可用的CPU内核,并处理并发的IO——所有不带有复杂的开发。相较于Python/Java,在一个goroutine上运行一个函数需要最小的样板代码。你只需使用关键词「go」添加函数调用:


  
    
    
    
  1. package main

  2. import (

  3.    "fmt"

  4.    "time")func say(s string) {

  5.    for i := 0; i < 5; i++ {

  6.        time.Sleep(100 * time.Millisecond)

  7.        fmt.Println(s)

  8.    }}func main() {

  9.    go say("world")

  10.    say("hello")}



Go 的并发性方法非常容易上手,相较于 Node 也很有趣;在 Node 中,开发者必须密切关注异步代码的处理。


并发性的另一个优质特性是竞赛检测器,这使其很容易弄清楚异步代码中是否存在竞态条件。下面是一些上手 Go 和通道的很好的资源:


  • https://gobyexample.com/channels

  • https://tour.golang.org/concurrency/2

  • http://guzalexander.com/2013/12/06/golang-channels-tutorial.html

  • https://www.golang-book.com/books/intro/10

  • https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html


原因 5:快速的编译时间


当前我们使用 Go 编写的最大微服务的编译时间只需 6 秒。相较于 Java 和 C++呆滞的编译速度,Go 的快速编译时间是一个主要的效率优势。我热爱击剑,但是当我依然记得代码应该做什么之时,事情已经完成就更好了。


Go 之前的代码编译


原因 6:打造团队的能力


首先,最明显的一点是:Go 的开发者远没有 C++和 Java 等旧语言多。据知,有 38% 的开发者了解 Java,19.3% 的开发者了解 C++,只有 4.6% 的开发者知道 Go。GitHub 数据表明了相似的趋势:相较于 Erlang、Scala 和 Elixir,Go 更为流行,但是相较于 Java 和 C++ 就不是了。


幸运的是 Go 非常简单,且易于学习。它只提供了基本功能而没有多余。Go 引入的新概念是「defer」声明,以及内置的带有 goroutines 和通道的并发性管理。正是由于 Go 的简单性,任何的 Python、Elixir、C++、Scala 或者 Java 开发者皆可在一月内组建成一个高效的 Go 团队。


原因 7:强大的生态系统


对我们这么大小的团队(大约 20 人)而言,生态系统很重要。如果你需要重做每块功能,那就无法为客户创造收益了。Go 有着强大的工具支持,面向 Redis、RabbitMQ、PostgreSQL、Template parsing、Task scheduling、Expression parsing 和 RocksDB 的稳定的库。


Go 的生态系统相比于 Rust、Elixir 这样的语言有很大的优势。当然,它又略逊于 Java、Python 或 Node 这样的语言,但它很稳定,而且你会发现在很多基础需求上,已经有高质量的文件包可用了。


原因 8:GOFMT,强制代码格式


Gofmt 是一种强大的命令行功能,内建在 Go 的编译器中来规定代码的格式。从功能上看,它类似于 Python 的 autopep8。格式一致很重要,但实际的格式标准并不总是非常重要。Gofmt 用一种官方的形式规格代码,避免了不必要的讨论。


原因 9:gRPC 和 Protocol Buffers


Go 语言对 protocol buffers 和 gRPC 有一流的支持。这两个工具能一起友好地工作以构建需要通过 RPC 进行通信的微服务器(microservices)。我们只需要写一个清单(manifest)就能定义 RPC 调用发生的情况和参数,然后从该清单将自动生成服务器和客户端代码。这样产生代码不仅快速,同时网络占用也非常少。


从相同的清单,我们可以从不同的语言生成客户端代码,例如 C++、Java、Python 和 Ruby。因此内部通信的 RESET 端点不会产生分歧,我们每次也就需要编写几乎相同的客户端和服务器代码。


使用 Go 语言的缺点


缺点 1:缺少框架


Go语言没有一个主要的框架,如Ruby的Rails框架、Python的Django框架或PHP的Laravel。这是Go语言社区激烈讨论的问题,因为许多人认为我们不应该从使用框架开始。在很多案例情况中确实如此,但如果只是希望构建一个简单的CRUD API,那么使用Django/DJRF、Rails Laravel或Phoenix将简单地多。


缺点 2:错误处理


Go语言通过函数和预期的调用代码简单地返回错误(或返回调用堆栈)而帮助开发者处理编译报错。虽然这种方法是有效的,但很容易丢失错误发生的范围,因此我们也很难向用户提供有意义的错误信息。错误包(errors package)可以允许我们添加返回错误的上下文和堆栈追踪而解决该问题。


另一个问题是我们可能会忘记处理报错。诸如errcheck和megacheck等静态分析工具可以避免出现这些失误。虽然这些解决方案十分有效,但可能并不是那么正确的方法。


缺点 3:软件包管理


Go语言的软件包管理绝对不是完美的。默认情况下,它没有办法制定特定版本的依赖库,也无法创建可复写的builds。相比之下Python、Node和Ruby都有更好的软件包管理系统。然而通过正确的工具,Go语言的软件包管理也可以表现得不错。


我们可以使用Dep来管理依赖项,它也能指定特定的软件包版本。除此之外,我们还可以使用一个名为VirtualGo的开源工具,它能轻松地管理Go语言编写的多个项目。

Python vs Go


我们实施的一个有趣实验是用Python写排名feed,然后用Go改写。看下面这种排序方法的示例:


  
    
    
    
  1. {

  2.    "functions": {

  3.        "simple_gauss": {

  4.            "base": "decay_gauss",

  5.            "scale": "5d",

  6.            "offset": "1d",

  7.            "decay": "0.3"

  8.        },

  9.        "popularity_gauss": {

  10.            "base": "decay_gauss",

  11.            "scale": "100",

  12.            "offset": "5",

  13.            "decay": "0.5"

  14.        }

  15.    },

  16.    "defaults": {

  17.        "popularity": 1

  18.    },

  19.    "score": "simple_gauss(time)*popularity"}



Python和Go代码都需要以下要求从而支持上面的排序方法:


  • 解析得分的表达。在此示例中,我们想要把 simple_gauss(time)*popularity 字符串转变为一种函数,能够把 activity 作为输入然后给出得分作为输出。

  • 在 JSON config 上创建部分函数。例如,我们想要「simple_gauss」调用「decay_gauss」,且带有的键值对为"scale": "5d"、"offset": "1d"、"decay": "0.3"。

  • 解析「defaults」配置,便于某个领域没有明确定义的情况下有所反馈。

  • 从 step1 开始使用函数,为 feed 中的所有 activity 打分。


开发Python版本排序代码大约需要3天,包括写代码、测试和建立文档。接下来,我么花费大约2周的时间优化代码。其中一个优化是把得分表达simple_gauss(time)*popularity转译进一个抽象语法树。我们也实现了caching logic,之后会预先计算每次的得分。


相比之下,开发Go版本的代码需要4天,但之后不需要更多的优化。所以虽然最初的开发上Python更快,但Go最终需要的工作量更少。此外,Go代码要比高度优化的python代码快了40多倍。


以上只是我们转向Go所体验到的一种好处。当然,也不能这么做比较:


  • 该排序代码是我用Go写的第一个项目;

  • Go代码是在Python代码之后写的,所以提前理解了该案例;

  • Go的表达解析库质量优越。


Elixir vs Go


我们评估的另一种语言是Elixir。Elixir建立在Erlang虚拟机上。这是一种迷人的语言,我们之所以想到它是因为我们组员中有一个在Erlang上非常有经验。


在使用案例中,我们观察到Go的原始性能更好。Go和Elixir都能很好地处理数千条并行需求,然而,如果是单独的要求,Go实际上更快。相对于Elixir,我们选择Go的另一个原因是生态系统。在我们需求的组件上,Go的库更为成熟。在很多案例中,Elixir 库不适合产品使用。同时,也很难找到/训练同样使用Elixir的开发者。


结论


Go是一种非常高效的语言,高度支持并发性。同时,它也像C++和Java一样快。虽然相比于Python和Ruby,使用Go建立东西需要更多的时间,但在后续的代码优化上可以节省大量时间。在Stream,我们有个小型开发团队为2亿终端用户提供feed流。对新手开发者而言,Go结合了强大的生态系统、易于上手,也有超快的表现、高度支持并发性,富有成效的编程环境使它成为了一种好的选择。Stream仍旧使用 Python做个性化 feed,但所有性能密集型的代码将会用Go来编写。


原文地址:

https://getstream.io/blog/switched-python-go/

登录查看更多
0

相关内容

Python是一种面向对象的解释型计算机程序设计语言,在设计中注重代码的可读性,同时也是一种功能强大的通用型语言。
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
173+阅读 · 2020年1月1日
从零开始用Python写一个聊天机器人(使用NLTK)
AI研习社
10+阅读 · 2018年12月27日
推荐一些适合小白练手的Python项目
数据挖掘入门与实战
6+阅读 · 2018年5月17日
Python3.7中一种懒加载的方式
Python程序员
3+阅读 · 2018年4月27日
快乐的迁移到 Python3
Python程序员
5+阅读 · 2018年3月25日
Python NLP入门教程
Python开发者
8+阅读 · 2017年11月19日
十分钟读懂python的“数据库”语言
Python技术博文
3+阅读 · 2017年11月9日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
代码这样写不止于优雅(Python版)
数说工作室
4+阅读 · 2017年7月17日
Arxiv
24+阅读 · 2020年3月11日
Arxiv
3+阅读 · 2018年12月18日
Arxiv
4+阅读 · 2018年10月31日
VIP会员
相关资讯
从零开始用Python写一个聊天机器人(使用NLTK)
AI研习社
10+阅读 · 2018年12月27日
推荐一些适合小白练手的Python项目
数据挖掘入门与实战
6+阅读 · 2018年5月17日
Python3.7中一种懒加载的方式
Python程序员
3+阅读 · 2018年4月27日
快乐的迁移到 Python3
Python程序员
5+阅读 · 2018年3月25日
Python NLP入门教程
Python开发者
8+阅读 · 2017年11月19日
十分钟读懂python的“数据库”语言
Python技术博文
3+阅读 · 2017年11月9日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
代码这样写不止于优雅(Python版)
数说工作室
4+阅读 · 2017年7月17日
Top
微信扫码咨询专知VIP会员