我們是怎麼升級到Python3的
最近我們從Python2.7, 全線升級到了Python3.5。
Python2 和 Python3 有啥區別啊?
在程序員的理想鄉里, 程序語言理應只是個好用的工具。 然而在現實生活里, 程序語言甚至是程序語言的版本, 關係到工程的很多方面。 Python2和Python3就像是小黃車和摩拜一樣, 在某幾個大的特性上有區別, 但本質上都一樣, 不過一些細微之處又有不同。
比如說 Python2 最坑的 Unicode. 寫過 Python2 的人總會遇到 UnicodeDecodeError 和 UnicodeEncodeError 這樣的錯。 在電話面試的時候, 我們回答候選人我們為什麼會用 Python2, 也是把鍋丟給謝老闆:
嗯是這樣的, 我們第一行代碼到整個初期框架都是 CEO 選的。 他是美國回來的, 所以不知道中國要用中文, 也會遇到 Unicode 相關的問題。 於是他沒想太多, 就選了 Python2.7.
真實原因還有很大一部分是因為當時一些庫對 Python2 支持比較好
除了 Unicode 的區別, Python2 到 Python3 還有一些系統自帶函數有變化。 比如 urllib.urlencode -> urllib.parse.urlencode, StringIO.StringIO -> io.BytesIO。 一般項目裡面可以用six這個庫來做兼容, 比如上面舉的兩個例子可以用six.moves.urllib.parse.urlencode 和six.BytesIO來替換, Django也自帶了一個six在django.utils.six. 不過我們是自己搭建環境, 所以其實不用考慮兼容性, 遷移工作大概的 Milestone 如下:
一些微小的工作
- 確保自己代碼的兼容性。
- 確保第三方庫的兼容性。
- 確保單元測試能過。
- 確保測試環境、集成測試能過。
- 正式環境切換 Python3!
- 宣布輝煌結果!
比如說講些我們具體做的事情吧:
柳老闆很早就想把謝老闆的Python2換了。 所以大概在16年底的時候, 我們都有這個心理預期。 因為我們都是 PyCharm 用戶, (JetBrains 家巨好用的 IDE) 所以都打開了 Python Compatibility Inspection。 然後寫代碼的時候注意不使用xrange(), dict.iteritems(), print , 而用range(), dict.items(), print()來代替。 對於Unicode一類的問題, 我們使用了__future__這個功能, 確保每個文件的頭都是:
# -*- coding: utf-8 -*-from __future__ import absolute_import, unicode_literals# 第一行指定 encoding# 第二行 absolute_import 指定優先從絕對路徑 import# 參見 PEP-0328: https://www.python.org/dev/peps/pep-0328# 第二行 unicode_literals 指定字元串默認使用 unicode 類型# 參見 PEP-3112: https://www.python.org/dev/peps/pep-3112
比如有個很蠢的py2to3的工具, 用它最好情況也只能把上面說到的「自己代碼和系統依賴」給替換了。 具體的庫替換、正確性驗證還是得自己做。
後來我們又大概過了一遍用到的第三方庫(requirements.txt)。 最大的依賴 Django 本身是2/3都兼容的, 然後像 celery/redis/requests 這種兼容性也妥妥的。 之前我們用的 AWS Python SDK - boto 不支持 Python3, 這個好說,對應的功能可以用boto3來替換。
比較麻煩的是當時用的微信庫python-wechat-sdk。 這個主要是我們用到了微信的很多功能: 消息回復、用戶管理、模板/圖文、各種素材等。 本身代碼量就不小, 而且之前的代碼寫的還比較屎, 亟需重構。 於是我們花了幾個月來重構代碼+換庫, 最終換成了更科學更好用的wechatpy。
代碼上的準備工作做完以後 (雖然這是輕巧的一句話, 但是因為實際開發中, 不可能空出一段時間專門用於架構升級。 所以我們都是在各種業務需求中找夾縫做的, 大概用了半年。)
代碼上的準備工作做完以後, 我們又用Python3跑了一下單元測試(UT)。 完善的自動測試,是平時的保障,是關鍵時刻的定心丸。 理論上 Python2 到 Python3, 不應當有任何外部表現差異, UT也不應當有錯。 所以在修復了UT的錯誤以後, 我們就有信心切換 Python3 跑跑了。
這裡還有個小差別, 就是我們之前的伺服器用的是 Ubuntu 14 (Trusty), Ubuntu14 上默認的 Python3 是 Python3.4。 最新的 Ubuntu 版本是 Ubuntu 16 (Xenial), 上面默認的 Python3 是 Python3.5。 柳老闆也心水了 Ubuntu16 很久了, 於是這次切換 Python3, 我們也順便把伺服器版本升到了 Ubuntu16。 不能print中文的feature也下掉了
然後就是測試環境切換 Python3, 生產環境切換 Python3 了。 這裡也有個小插曲, 生產環境切換 Python3 的時候, 我們原本想著先只升級一部分Server(灰度), 但是 Celery 的 Python2 Producer拋出的任務, Python3 Consumer能接, 但是百分百完不成…
於是我們決定這還灰個蛋,不灰了! 找一天晚上點份奶茶和小龍蝦, 全線切換Python3! 因為做好了前期準備, 所以我們大概只花了十分鐘切換成了Python3~ 腰也不酸了, 頸椎也不疼了, 連美餐的飯也好像變得美味了起來。 現在的我司後台就是跑在Python3上的~
最後還有就是大家的開發環境也切換一下 Python3 啦, 以前寫的 docstring 可以切換成 type hintings 啦之類的微小的工作了。
# Python 2 的時候這麼寫def old_hint(messages, data=()): """ :type messages: list :type data: list[dict] :rtype: str """ pass# Python 3 就可以這麼寫def new_hint(messages: list, data: list(dict) = ()) -> str: pass
總結
從 Python 2 到 Python 3 的好處見仁見智, 對於不同的情況各有不同。 做成這麼一件微小的事情, 感受比較深的就是:
- 目標要清晰。 比如我們早早地就把 Python 3 提到了我們的 Scrum Board 上。 除了技術很清楚我們要做什麼, 產品也會有意識地給我們升級架構留下排期。 目標清晰,大家步伐就會一致。
- 要有推動者。 比如柳老闆就充當了整件事情的推動者, 有序給大家分鍋,具體哪個模塊誰來重構,哪個庫誰來換。 一些沒人想乾的麻煩/要背鍋的事情他也都做了, 比如去修UT的兼容性之類的…
- 互相信任真好。 後端升級架構, 其實各方面都會受影響, 比如上文提到的 Celery 那個坑就讓我們緊急回退了版本, 或者類比一下遊戲廠商每周例行的停伺服器操作。 這時候產生的一些鍋, 就被一線的隊友背了, 他們也沒多埋怨就幫我們擦屁股去了… ORZ 感恩!
不論如何,Python 3畢竟是趨勢, Django的新版本不會支持Python 2, Celery的新版本也不會支持Python 2了。 我們也不能落後呀。
就像陳皓老師常說的一樣:
技術債是還不完的,但我們也要一直還!
陳皓老師:不,我沒說過這句話,你自己編的。
推薦閱讀:
※Python · 進度條
※Spark SQL你不得不知道的那些事兒
※Python實現Zip文件的暴力破解
※Python篇-多進程與協程的理解與使用
※如何踏上人工智慧之路(機器學習篇)