我尝试将一个我个人开发的Python3项目改写为Golang
我是TuneCoreJapan的新入职工程师,名字叫skfvr。
这篇文章是Wano集团Advent Calendar 2019第4天的文章。
觸發這個話題的動機是什麼?
目前我所分派到的項目基本上是使用 Perl 和 Ruby 进行开发,但我作为新人被分派的时候,“使用 Golang 进行替代”这个计划已經正式启动了。
我之前尝试将我个人开发的项目(用Python3开发)全部改成Go语言,感觉如何呢?让我来谈谈我在这个过程中的感受。
请注意
-
- どちらかの言語を上げ(下げ)する目的の記事では無いことを先に断っておきます(不快に感じる方が居たらごめんなさい)。
- 初投稿故、誤字/脱字/知識不足を暖かく見守っていただけると幸いです。知識不足の祭は指摘していただけると嬉しいです。
最初它原本是用来控制什么样的东西的?
个人开发虽然被称为,但规模并不大,没有取得什么重大进展。
-
- Slack incoming webhook / slash command
-
- Twitter Bot
- その他…
虽然我认为这并没有什么参考价值,但文件数量不超过100个,行数也不多。
SlackBot的功能涵盖了我自己制作的TODO管理和邮件管理等内容。
TwitterBot则使用了一定程度的功能,包括获取推文、发布推文和关注等。
换了真是太好了!
从一开始还不错的地方开始说起。
在Go的一侧帮我进行各种检查。
我认为切换到Go语言的好处在于go fmt等工具的存在。
-
- gofmt : 自動的に整形してくれる(公式で提供されるので個々人の間でブレない)。
-
- goimports : 使用しているパッケージをコードから勝手に import してくれる。
-
- go mod : 使用しているパッケージを良い感じに管理してくれる。
- 他、開発者が作成した linter など:未使用変数や error 確認忘れなどを検査できる。
个人使用这个工具已经足够了,尤其是像gofmt这样的官方提供的工具,使得即使是团队开发也不必担心出现不一致的问题,这一点非常重要。
即使在简单地编写代码时,我们也不需要手动添加等号两边的空格,因为之后会使用gofmt/goimports来自动格式化代码。
我认为,如果使用有志开发的静态分析工具,可以进行更高级的检查,这样可以更容易保持代码的质量。对于多人开发的情况,这应该特别有用。
此外,我认为Go语言对于类型非常严格。因此,“直到执行才知道”这种情况变得更少了。当使用interface{}等时,我会有些紧张。
错误
以前,发现在函数的返回值中同时返回错误值 err 的方式让我感到非常赞叹(在好的方面)。例如,有一个将字符串转换为数字的函数 strconv.Atoi(string)(int, error)。简而言之,如果转换失败,就会返回 error!=nil 的值,我们将其接收。
hoge, err := strconv.Atoi("1")
// => hoge == 1, err == nil
// 変換が成功したら変換成功したときの値と err(==nil) が返ってくる
hoge, err := strconv.Atoi("hoge")
// => hoge == 0, err == NumError
// 変換ミスで err が返ってくる
相比个人尝试使用try-catch,了解返回的位置和值等非常明确易懂,这一点很好。另外,使用err也更容易编写日志部分。如果有意忽略,可以用下划线接收数据,这样就可以了。
hoge, _ := strconv.Atoi("5")
try:
hoge = str("hoge")
except:
# try-catch が多くなると辛い('ω`)...
特别是一个函数返回错误值,这样理解起来也更容易明白它可能会返回错误。
不过要注意,有些函数可能不会返回错误而是引发 panic 并终止程序…。
运行速度快
我觉得这里没什么好说的…
我在毕业论文中使用Python3编写了一个模拟程序,每次运行大约需要1分钟…可能我说得有点夸张,但是毕业论文结束后重新编写后,程序运行只需几秒钟而已…。
在假设只比较Python3和Golang的情况下,如果要求速度,那么可以说Golang是比较出色的选择,尽管它们基本上采用了相同的算法。
在修改时遇到了困难的地方
接下来将介绍我在改写这部分时所遇到的困难。
细微的部分
例如当你想要求一个最大值的时候。
func Max(x, y float64) float64
在Python3中。
max(1,2) => 2
max(1,3,2,7,6,5,9,8,4) => 9 # 複数 ok
int_list = [1,3,5,9,7]
max(int_list) => 9
# リストならそこから最大値を取得する
可以有各种不同的写法…。
尽管在Golang中有len函数,但最初我还记得我认为min和max函数很不好用!
接下来是关于字符串操作部分…。
在Python中
hoge = "bar,piyo,foo"
splited = hoge.split(",")
针对Golang的情况而言
import strings
hoge := "bar,piyo,foo"
splited = strings.Split(hoge, ",")
这个地方经常会弄错。(’ω`)
这种常用的函数确实很难打消掉习惯。这个地方是艰难点积攒积分的地方。
我想写列表生成式(个人愿望)。
在开始改写前的一段时间里,我对此感到非常厌烦,简直到了过敏的程度。因为在开始认真学习Golang之前,我一直在写Python3,所以在这里也遇到了很多困难的点。
hoge_list = [i for i in range (10) if i%2==0]
我记得我已经太习惯于这个了,所以当我要用Golang进行相同的操作时感到非常困难。
当然,除了列表推导之外,在使用Python时还有其他一些我频繁使用但有些“冷门”的语法,所以在不使用它们的情况下进行重写时,我费了很大的力气。
需要找到必要的软件包等。
(Pinyin: de .)
这是发生在从一种语言转换为另一种语言时,不论是Python还是Golang,都会遇到的情况。
举个例子,由于我自己所创造的东西需要加入Twitter、Slack等服务,以及MeCab等外部的形态素解析,所以。
-
- mecab
-
- Twitter
-
- Slack
-
- Mysql
- Firebase
使用了大概五个左右的包,所以我认为在寻找包的过程中很重要。最糟糕的是,“在之前的语言中能找到 XXXX 的 API 包,但在新的语言中却找不到!”之类的情况也有可能发生。
在这个领域进行详尽的调查是必要的。特别是如果迁移的目标语言是非常新的语言,真的有可能找不到包…。
选项 1: 外传
这是与主要情节关系不大的番外篇。
當進行競技程式設計時
如果我参与竞技编程等活动,例如,如果我需要接收一个整数数组。
# ipunt > 1 3 4 2 6 8 1 2 3
hoge = list(map(int, input().split()))
我之前常常这样做(也许有更好的方法吗?)。
虽然我从未尝试过在Golang上进行竞技编程,但是我想知道该如何进行…。
对于换人的感觉和经验
-
- (もしクソコードが存在して入れば)クソコードを一掃することができるチャンス。
-
- 覚悟は必要(規模としては小さいけどチマチマやって1か月かかった)。特に細かい部分の癖は抜けない。
外部サービスなどのパッケージは絶対に調査しておこう。また、ビルトインでも「なんでこれがないの…」もあるかもしれない。
リスト内包表記中毒が治らない
我认为第三项尤为重要。「如果语言转移后没有相应的支持包!」这种情况很有可能发生。
如果对您有所帮助,我会非常高兴。
我目前正在業務中進行從 Perl 到 Golang 的遷移,希望明年這個時候可以獲得這方面的經驗並升級改進。