Categories
程式開發

為什麼遷移至Python 3這麼難?


2020年1月1日,Python 2的生命週期正式截止,Python核心開發人員也宣布即將不再提供該版本的安全更新,並建議用戶盡快遷移至Python 3。然而問題也在此時出現了,不論Python 3有多少優點,遷移的過程對於用戶來說都極其痛苦,但是如果不這麼做的話,又會有其他問題出現。對此,開發者只剩一聲長嘆,難啊!

遷移至Python 3,用戶怨聲載道

2020年3月4日,一位用戶在Twitter上吐槽:

image

他的大概意思是:“每個人都堅持要擺脫Python 2,但這樣的做法卻將我們擁有的所有功能完善且有用的Python 2代碼從資產變成負債。”

隨後他又舉例說:

image

“自2011年用Python 2編寫以來,我的milter實現一直是完全穩定的。現在,我不得不破壞它的穩定性,因為Python 2沒法用了。”

有同樣感受的人不只是他一個,尤其是那些大公司的開發人員。

2013年,Facebook計劃將代碼遷移至Python 3,但是從產生這個想法到真正交付,一共花費了四年的時間,代碼遷移僅僅是難題的開始,更重要的是讓自己的員工使用並適應Python 3;另有LinkedIn進行的“曠日持久戰”,550個代碼存儲庫(庫、應用程序和服務)要遷移、上百萬行的代碼要處理,還有內部用於持續集成/持續交付(CI/CD)框架、命令行接口以及部署和數據科學工具,這種散亂的非整體式環境用Warsaw的話來說“包括數百種獨立的微服務和工具,外加幾十個支持庫。

如此浩瀚的工程,花費的時間、精力都是巨大的,參與其中的人怨聲載道,甚至有人將其稱之為“噩夢般的工作”。然而當龐大的遷移終於完成,新的問題又出現了。

跨平台的分佈式版本控制軟件Mercurial就是Python編寫的,所以對於向Python 3的遷移,Mercurial也是非常的積極。在經歷了同樣大規模的遷移後,問題暴露了出來,負責Mercurial運維工作的工程師Gregory Szorc在博客上進行了一番吐槽:

簡而言之,我將Mercurial和其他項目移植到Python 3的經驗極大地破壞了我對Python的理解。從語言到熱情的社區,我一直以來都對Python充滿愛,但我仍在努力理解Python如何通過選擇他們所做的過渡計劃來設法給社區帶來如此多的困難。

Python 3.0於2008年12月3日發布,社區花了十年的時間來接受它。這應該被普遍認為是失敗的。

我真的對Python很不滿意。移植到Python 3所需的工作量驚人。對於Mercurial而言,Python 3引入了很多問題,但並不能解決很多問題。我們在泥濘中摸爬滾打了好幾年,直到最終陷入比我們開始時更糟的狀態。我敢肯定,幾年後它將變得更好。但是在此之前,我們要經歷5年以上的過渡期。官方宣稱Python 3過渡會對項目造成破壞和乾擾,這是一種太輕描淡寫的說法。

問題出在哪裡?

Python 3的遷移為什麼如此困難?回答這個問題之前,我們先簡單了解一下它誕生的背景。

自 2008 年發布以來,Python 2.0 已經走過了十多個年頭。它的最後一次重大更新 ——Python 2.7 是在 2010 年。

雖然Python 2.x 是一個還不錯的版本,但同時也帶來了相當大的歷史包袱,例如,它有兩種整數類型;存在惱人的Unicode 編碼問題;它混淆了懶惰和渴望的功能工具;它有一個標準的庫,但加載內存非常龐大;它自詡的強類型,卻有偶爾令人啼笑皆非的運算結果None < 3 < “2”。總的來說,它的一些“陰暗角落“,包含了 Python 1 時代太多的歷史包袱。

由於修復這些問題可能會破壞現有代碼,而幾乎所有為 2.0 編寫的代碼仍可在 2.7 上運行,Python 3 應運而生。

image

設計之初,Python 3 的預期是用戶會直接轉移到新版本,從而放棄使用 Python 2 。然而在一開始,人們有很多理由不採用Python 3:最主要的原因是,它並沒有與Python 2兼容。同時,大多數的庫希望同時在 Python 2 和 Python 3 上運行,這在一開始很難運作,並且由於缺乏支持工具,移植代碼的工作十分艱難。

轉折點發生在大約2016年左右的Python 3.5發行版中,該版本增加了矩陣乘法、引入了asyncio、對OrderedDict的速度進行了提升以及實現了類型提示,這些提示為Python帶來了一些類似於靜態語言的功能。

更高版本包含更多功能,例如Pathlib庫和 f 字符串操作。通過這些更改,人們使用的許多庫(例如用於機器學習的scikit-learn )開始向Python 3遷移。

除了Python 3本身存在的一些技術問題,用戶不願意遷移的主要原因還有這樣幾點:

安全問題。具有諷刺意味的是,用戶會認為不進行升級會帶來更大的風險,但是在大型組織或機構中,不允許員工自己升級Python:管理員或安全團隊會向他們推送更新。在某些情況下,也不允許下載PIP。如果Python 2是安全團隊同意的默認設置,那麼它可能需要做出巨大的努力才能說服人們將其切換到3,尤其是在受到嚴格監管(例如醫療保健或金融)和政府的環境中。

使用慣性。儘管許多版本的Linux中(例如RHEL),都同時兼容Python 2與Python 3,但這不是默認的選項,因此用戶在2和3之間切換時,經常發現一些錯誤,尤其是指向系統版本的指針,例如,在Debian上使用Python。

如何避免遷移出現問題?

如何遷移到Python 3?每家公司的做法可能有所不同,所以在此之前,不如先看看官方給出的建議。

Python軟件基金會已經為需要同時運行Python 2和3的組織提供瞭如何實現跨代兼容性的綜合指南,以下是其建議的摘要:

1.放棄對Python 2.6和更早版本的支持,因為從Python 2.7遷移要容易得多,並且如果必須運行Python 2.6,請考慮使用 six library 來與Python 3兼容。

2.確保setup.py文件正確指定了代碼庫支持的Python版本,並且該文件至少包括Programming Language :: Python :: 2 ::僅作為trove分類器。

3.測試套件應至少具有80%的代碼覆蓋率,即在測試過程中執行多少源代碼的名稱。如果不了解代碼覆蓋率,請使用coverage.py提供的工具。

4.閱讀Python的“新增功能”文檔和免費的“ 移植到Python 3”手冊,了解Python 2和Python 3之間的區別。

5.使用FuturizeModernize使Python 2代碼與Python 3兼容,請確保閱讀文檔,以便解決這些無法處理的問題。

6.確保適應在Python 2和3之間處理整數除法的更改。例如,在Python 2 中 9/2 = 4,而在Python 3 中 9/2 = 4.5。如果可以在代碼中使用“future import”除法和“//”運算符進行整數除法,那麼代碼已經與Python 3兼容。

7.Python 3更改了可以與str類型一起使用的數據,以使文本和二進制數據之間的區別更加清晰。不幸的是,對於同時處理文本和二進制數據的代碼,必須執行以下步驟以確保代碼符合要求:https://docs.python.org/3/howto/pyporting.html#text-versus-binary-data

8.當運行的代碼因運行的版本不同而表現不同時,最好檢查Python 3支持的特定功能是否能夠運行,而不是檢查sys.version_info [0]是否等於3。

9.為了幫助對齊用Python 3編寫的任何新代碼並確保其兼容性,請在創建的任何新模塊的頂部使用以下語句:from future import absolute_import, from future import division, 以及 from future import print_function。

10.使用 caniusepython3 提供的工具,檢查哪些軟件依賴項會阻止支持Python 3 。

11.遷移代碼後,請在setup.py文件中更新分類器,使其包含Programming Language :: Python :: 3,以表明代碼支持Python 2和3。

12.通過使用tox自動化測試並將此設置與持續集成系統進行集成,以確保代碼與Python 2和3保持兼容。

另外,為了將Python 2遷移到Python 3代碼,NCSC還建議使用2to3應用程序,該應用程序將嘗試自動執行該過程。

總而言之,雖然很麻煩,但是為了保持功能的先進性,企業或個人開發者還是不得不選擇遷移到Python 3。如果想避免遷移過程出現問題,可以參考一些大公司的遷移情況,這裡有一些案例可供參考,希望可以幫到廣大開發者: