Python 2 還能走多遠(yuǎn)?
作者丨cunshun
導(dǎo)語(yǔ) 自2020年P(guān)ython 2停止維護(hù)至今,5年過(guò)去了。雖然還有很多項(xiàng)目依舊在使用Python 2,但不可否認(rèn),無(wú)論是企業(yè)還是社區(qū),都在努力向Python 3邁進(jìn)。Python 2簡(jiǎn)潔易讀、靈活易上手、支持庫(kù)豐富,但隨著大規(guī)模使用,亦逐漸暴露出各種不足。尤其是近些年P(guān)ython維護(hù)者們通過(guò)持續(xù)補(bǔ)丁對(duì)Python 2進(jìn)行優(yōu)化和修復(fù),但其底層架構(gòu)和實(shí)現(xiàn)上的問(wèn)題依然無(wú)法完全解決。為什么要升級(jí)到Python 3?如何在TencentOS Server 4中進(jìn)行升級(jí)?

最近在做TS4的升級(jí)工作,遇到比較多的問(wèn)題是:
- Python2不能用了嗎?
 - Python3有啥好處,為啥要升?
 - 有沒(méi)有什么方法一鍵升級(jí)到Python3?
 
這里在開(kāi)頭先簡(jiǎn)單統(tǒng)一回答一下:
- Python2停止維護(hù)后積累很多的安全漏洞、bug,官方也不再修復(fù),存在嚴(yán)重的使用風(fēng)險(xiǎn),不建議繼續(xù)使用。
 - 升級(jí) Python3 可以獲得更全更新的模塊支持、更強(qiáng)的性能、更好的編程語(yǔ)法、更長(zhǎng)的維護(hù)保障等等。
 - 沒(méi)有一鍵升級(jí)的辦法,簡(jiǎn)單的python代碼或許可以,復(fù)雜一點(diǎn)的項(xiàng)目需要搭配多種手段來(lái)適配。
 
接下來(lái)我們針對(duì)上面的問(wèn)題,具體展開(kāi)。
一、Python 2 的前世
1. Python 2的發(fā)布
在 Python 2 之前,還有一個(gè)在 1991 年 2 月 發(fā)布的 Python 0 和 在1994 年 1 月發(fā)布的 Python 1。一些核心功能在Python0 上已經(jīng)具備,例如異常處理、函數(shù)的類以及各種核心數(shù)據(jù)類型,如 list、dict 和 str等等。
Van Rossum first published the code (for Python version 0.9.1) to alt.sources in February 1991. Several features of the language were already present at this stage, among them classes with inheritance, exception handling, functions, and various core datatypes such as list, dict, and str. The initial release also contained a module system borrowed from Modula-3; Van Rossum describes the module as "one of Python's major programming units". Python's exception model also resembled Modula-3's, with the addition of an else clause. In 1994 comp.lang.python, the primary discussion forum for Python, was formed.
Python1,又加入了一些函數(shù)式編程工具 lambda、map、filter 和 reduce的功能。
Python reached version 1.0 in January 1994. The major new features included in this release were the functional programming tools lambda, map, filter and reduce. Van Rossum stated that "Python acquired lambda, reduce(), filter() and map(), courtesy of a Lisp hacker who missed them and submitted working patches".
Python2,在2000年10月發(fā)布,從此一個(gè)持續(xù)了20年的版本帝國(guó)拉起帷幕。
Python 2.0, released October 2000,[8] introduced list comprehensions, a feature borrowed from the functional programming languages SETL and Haskell. Python's syntax for this construct is very similar to Haskell's, apart from Haskell's preference for punctuation characters and Python's preference for alphabetic keywords. Python 2.0 also introduced a garbage collector able to collect reference cycles.
2. Python 2的演進(jìn)
下面這個(gè)表展示了整個(gè)Python的版本發(fā)布?xì)v程,看到每個(gè)版本發(fā)布的間隔和到終止維護(hù)基本上在5年內(nèi),但是只有最后一個(gè)2.7版本,在2010年發(fā)布,又繼續(xù)維護(hù)了10年之久,甚至很多人都覺(jué)得Python2會(huì)一直維護(hù)下去。

看下面這張圖時(shí)間線更清楚一點(diǎn)。python2.7很長(zhǎng)一段時(shí)間內(nèi)和python3共存,所以這段時(shí)期在各大主流發(fā)行版中,都會(huì)同時(shí)提供 Python2 和 Python3 兩個(gè)Python版本。

來(lái)源:https://zh.wikipedia.org/wiki/Python%E7%9A%84%E6%AD%B7%E5%8F%B2
二、Python 2 的今生
1. Python 2 的最后一舞
Python2官方一開(kāi)始宣布在2020年1月1日停止維護(hù)。https://pythonclock.org/
但是后來(lái)PSF將Python2的實(shí)際停用時(shí)間更新為2020年4月。
https://www.python.org/psf/press-release/pr20191220/
這一舉措引起了一些Python使用者的不滿,甚至發(fā)起了請(qǐng)?jiān)?,要求取?.7.18版本的發(fā)布。
https://discuss.python.org/t/petition-abandon-plans-to-ship-a-2-7-18-in-april/2946
后來(lái)官方解釋的說(shuō)法是2.7.18的代碼倉(cāng)在2020.1.1已經(jīng)被凍結(jié),但是版本發(fā)布在4月。
不管怎樣,這個(gè)巨無(wú)霸版本最終還是在2020年落下帷幕,2.7.18,作為Python2的最后一個(gè)版本,在2020年4月發(fā)布,同月,Python2.7的維護(hù)者在2020年4月發(fā)布一封郵件,正式和Python2道別,并呼吁開(kāi)發(fā)者從Python2遷移到Python3。
PSF官網(wǎng)也發(fā)布Python2停止維護(hù)的公告:https://www.python.org/doc/sunset-python-2/
2. 大勢(shì)所趨?
雖然社區(qū)呼吁大家切換到Python3,那實(shí)際情況是什么樣子呢?Python官方給出了一些項(xiàng)目的Python使用時(shí)間線。


在Python公布出來(lái)的一些項(xiàng)目名單中,可以看到很多項(xiàng)目(sphinx, pillow,pandas,numpy,apache spark等),在20年后的新版本,就不再支持python2,而僅支持Python3了(綠色表示兼容python2,藍(lán)色表示只支持python3)。包括后續(xù)新增的項(xiàng)目,都會(huì)優(yōu)先基于python3來(lái)構(gòu)建。

提示:更多項(xiàng)目宣布后續(xù)會(huì)不再支持python2: https://python3statement.github.io/
三、Python 2不能用了嗎?
Python2已經(jīng)挺好了,為什么還要造一個(gè)差別這么大的Python3?我的項(xiàng)目在Python2上跑的好好的,為什么要費(fèi)勁巴拉地適配Python3?
估計(jì)大多數(shù)人都會(huì)有上面這些疑問(wèn),Python維護(hù)者們也知道要讓開(kāi)發(fā)者從2切到3,就必須解釋這些問(wèn)題,如果有時(shí)間,大家可以去看看Python維護(hù)者們針對(duì) Python3 存在的一些文章和郵件,里面有很多細(xì)節(jié)。這里主要列出幾個(gè)普通開(kāi)發(fā)者遇到最多的問(wèn)題:
3. Python 2 的文本和二進(jìn)制處理混亂
Python2中的 str 同時(shí)承擔(dān)了 “文本”和“字節(jié)”兩種語(yǔ)義,才導(dǎo)致了 Python 3 的改進(jìn):明確區(qū)分文本(str)和字節(jié)(bytes)。例如下面這個(gè)字符串,在Python3里,他只是表示由“a”、“b”、“c”和“d”組成的字符串。
'abcd'但是在Python2里他卻可能是代表 97、98、99 和 100 的字節(jié),所以在Python2里,你既可以把他當(dāng)字節(jié)用也可以把他當(dāng)字符串用,很容易出錯(cuò)。但是在Python3中,做了嚴(yán)格的區(qū)分,這樣就可以保證代碼中使用不會(huì)出現(xiàn)問(wèn)題。
操作  | Python2行為  | Python3行為  | 
  | 合法,結(jié)果是'abcd' + 'efg'  | 合法,結(jié)果是'abcd' + 'efg'  | 
  | 合法,結(jié)果是'abcd' + 'efg'  | 報(bào)錯(cuò),類型不兼容  | 
  | 等價(jià)于'abcd'  | bytes類型  | 
2. Python 2 對(duì)Unicode支持不足
從上面的python版本生命線可以看到,python在1991年2月第一次發(fā)布,早于 1991 年 10 月發(fā)布的 Unicode 標(biāo)準(zhǔn)第一卷。所以Python2開(kāi)始只支持ASCII,后續(xù)才加入了對(duì)Unicode的支持。這就導(dǎo)致在Python2里面處理Unicode比較麻煩,你要處理中文、日文等非 ASCII 字符,必須用 unicode 類型,并且要手動(dòng)編碼/解碼,一不留神就會(huì)出錯(cuò)。
# Python 2 
s = '你好'  # 這是utf-8編碼的字節(jié)串,不是Unicode字符串 
print(type(s))  # <type 'str'> 
print(s)        # 可能亂碼,或者報(bào)錯(cuò) 
# 正確做法 
u = u'你好'     # Unicode字符串 
print(type(u))  # <type 'unicode'> 
print(u)        # 正常顯示下面是一個(gè)demo,展示了Python2里面如何對(duì)字符串進(jìn)行編解碼。
# 假設(shè) s = '中文',文件頭聲明了 utf-8 
s = '中文'              # s 是 str 類型,utf-8 編碼的字節(jié)串 
u = s.decode('utf-8')   # u 是 unicode 類型 
print(type(u))          # <type 'unicode'> 
# 反過(guò)來(lái) 
s2 = u.encode('utf-8')  # s2 是 str 類型 
print(type(s2))         # <type 'str'>參考:https://snarky.ca/why-python-3-exists/
四、Python 3有啥好處?
1. Python 3的優(yōu)化
Python3,在性能方面做了很多改進(jìn),下面列出了其中一些。
(1) 對(duì)象的 __dict__ 共享鍵存儲(chǔ)(PEP 412, Python 3.3)
- 多個(gè)同類型對(duì)象的 __dict__(屬性字典)可以共享鍵的存儲(chǔ)空間。
 - 優(yōu)點(diǎn):當(dāng)你創(chuàng)建大量自定義對(duì)象時(shí),節(jié)省內(nèi)存并提升訪問(wèn)速度。
 
(2) OrderedDict 用 C 實(shí)現(xiàn)(Python 3.5)
- collections.OrderedDict 由純 Python 實(shí)現(xiàn)變?yōu)?C 實(shí)現(xiàn)。
 - 優(yōu)點(diǎn):性能提升 4 到 100 倍,插入和遍歷更快。
 
(3) GIL(全局解釋器鎖)行為更可預(yù)測(cè)(Python 3.2)
- 舊版 GIL 按 Python 字節(jié)碼數(shù)讓出控制權(quán),耗時(shí)不穩(wěn)定。
 - 新版 GIL 按時(shí)間片(默認(rèn)5ms)讓出控制權(quán),線程調(diào)度更公平、可預(yù)測(cè)。
 
(4) io 庫(kù)用 C 重寫(Python 3.1)
- Python 2.7 的 io 庫(kù)是純 Python 實(shí)現(xiàn),Python 3.1 起用 C 實(shí)現(xiàn)。
 - 優(yōu)點(diǎn):文件和流的讀寫操作更快。
 
參考:https://eev.ee/blog/2016/07/31/python-faq-why-should-i-use-python-3/
2. 業(yè)內(nèi)的應(yīng)用實(shí)例
同時(shí),業(yè)內(nèi)已經(jīng)有不少企業(yè)通過(guò) Python2 切換到 Python3 獲得了性能和資源利用上的提升。例如 Instagram,通過(guò)適配Python3,獲得了 12% CPU 性能提升以及 30% 的內(nèi)存節(jié)省效果。

數(shù)據(jù)來(lái)源:https://www.youtube.com/watch?v=66XoCk79kjM&t=2346s
3. benchmark實(shí)測(cè)
Python 官方在版本發(fā)布時(shí),會(huì)對(duì) Python 版本進(jìn)行性能測(cè)試:
Python 3.11 的速度比 Python 3.10 快 10-60%。在平均狀況下,在標(biāo)準(zhǔn)基準(zhǔn)測(cè)試(standard benchmark suite)中可見(jiàn)1.25倍的加速效果。
https://docs.python.org/zh-cn/3.11/whatsnew/3.11.html#summary-release-highlights
雖然實(shí)際到業(yè)務(wù)環(huán)境中的性能提升可能沒(méi)有 benchmark 提升那么大(受代碼影響),但是在數(shù)據(jù)上一定是Python3的性能表現(xiàn)更好。
我們也基于一些Python常用軟件跑了benchmark測(cè)試套,結(jié)果顯示單純基礎(chǔ)性能 Python3 也比 Python2 要強(qiáng)很多。
TS4(TencentOS Server 4) 搭載了新版本 Python3,而 TS2(TencentOS Server 2) 則還是使用 Python2


4. 更多、更好的模塊支持

除了上面提到一些老組件后續(xù)不會(huì)支持python2,只支持python3之外。因?yàn)榻鼉赡闍I勢(shì)頭興起,AI相關(guān)的組件Python占大頭,這些Python組件大部分也只支持Python3。
(1) Pytorch
例如Pytorch,可以看到Pytorch不僅標(biāo)注了需要Python3.9以上版本,并且也在刪除了原來(lái)的python2兼容代碼:https://github.com/pytorch/pytorch?tab=readme-ov-file#prerequisites
# pytorch
If you are installing from source, you will need:
- Python 3.9 or later(2) vllm
包括vllm也只支持Python3。https://github.com/vllm-project/vllm/blob/main/pyproject.toml
[project]
name = "vllm"
...
classifiers = [
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
...
]
requires-python = ">=3.9,<3.13"
dynamic = [ "version", "dependencies", "optional-dependencies"]五、如何切換到Python 3
1. python 2項(xiàng)目適配到python 3的方法(推薦)
(1) python2移植python3指南:https://portingguide.readthedocs.io/en/latest/index.html
(2) python2兼容python3工具:
- https://github.com/PyCQA/modernize
 - https://github.com/benjaminp/six
 
(3) python2->python3轉(zhuǎn)化工具:https://docs.python.org/3.12/library/2to3.html
上面提到的 modernize 工具,一樣也有從將代碼從python2自動(dòng)轉(zhuǎn)化為python3的功能,只是后者會(huì)偏向于做一些能夠兼容python2的修改。
(4) 格式和錯(cuò)誤檢查:https://github.com/astral-sh/ruff
(5) 類型檢查:https://github.com/microsoft/pyright
(6) 測(cè)試套:
- 因?yàn)楣ぞ咿D(zhuǎn)化不是100%覆蓋,所以可能還需要一定的測(cè)試用例保證功能正常
 - 可以用pytest等測(cè)試用例來(lái)進(jìn)一步保證代碼正常
 
遷移思路:
- 如果決心遷移到Python3,可以好好看一遍Python2->Python3的指南,然后再通過(guò)2to3/modernize轉(zhuǎn)換,轉(zhuǎn)換之后通過(guò)ruff/pyright工具進(jìn)行二次確認(rèn)檢查,最后運(yùn)行自己的測(cè)試代碼保證功能正常。
 - 如果還是想先用Python2過(guò)渡,可以通過(guò)python-six工具兼容一些Python2和Python3之間的不兼容庫(kù)接口。
 
2. TS 4上安裝運(yùn)行Python遷移工具
TencentOS Server 4下運(yùn)行含有python2語(yǔ)法的代碼會(huì)報(bào)錯(cuò),下面的鏈接中的教程可以將python2的代碼轉(zhuǎn)化為python3。
https://docs.python.org/zh-cn/3.7/library/2to3.html
其中2to3這個(gè)命令在TencentOS Server 4中由 python3-devel 提供,要使用該命令需要安裝此包。
dnf install python3-devel -y六、尾聲
來(lái)自本文作者的話:
其實(shí)新舊版本更替,是所有軟件的必經(jīng)之路。我們?cè)陂_(kāi)發(fā)TencentOS Server 4發(fā)行版時(shí),也經(jīng)歷了類似的歷程
從2000年到2020年這20年間,正是互聯(lián)網(wǎng)爆發(fā)式發(fā)展的時(shí)期。數(shù)以萬(wàn)計(jì)的項(xiàng)目看中了Python簡(jiǎn)潔易讀、靈活易上手、支持庫(kù)豐富的特點(diǎn),選擇基于Python構(gòu)建開(kāi)發(fā)。許多大公司也積累了大量的Python代碼。作為這個(gè)時(shí)代的產(chǎn)物,Python 2被廣泛使用,甚至伴隨了許多Python開(kāi)發(fā)者的職業(yè)生涯。
隨著大規(guī)模的應(yīng)用,Python維護(hù)者意識(shí)到了 Python2 本身的一些不足,雖然在近20年的時(shí)間里一直通過(guò)補(bǔ)丁的方式來(lái)對(duì)Python2的進(jìn)行優(yōu)化修補(bǔ),但是底層架構(gòu)和實(shí)現(xiàn)上的問(wèn)題難以徹底解決的。所以Python維護(hù)者們依然決定開(kāi)啟新的Python項(xiàng)目,來(lái)從根本上解決這些問(wèn)題。
正是因?yàn)檐浖w速發(fā)展,老版本系統(tǒng)存在一些從根本上無(wú)法解決的問(wèn)題,可能是軟件本身,也可能是一些政治經(jīng)濟(jì)上的原因,所以才會(huì)有版本升級(jí)的存在。業(yè)務(wù)一旦跑起來(lái)就不想升級(jí)很正常,但是在技術(shù)更迭的大潮流下,不斷更新接納新的事物也是業(yè)務(wù)能持續(xù)發(fā)展的立身之本,畢竟誰(shuí)都不想做那個(gè)被拍在沙灘上的人是吧。















 
 
 




 
 
 
 