在剛剛過去的 2018 年里,要說最熱門的科技領(lǐng)域是哪一個(gè)?毋庸置疑的是,人工智能必排在前列;而要論編程語言界,最流行的編程語言是誰?那非 Python 莫屬。2018 年 8 月,根據(jù)一年一度的IEEE Spectrum 編程語言來看,Python 一路高歌猛進(jìn),位居 48 種編程語言之首。不僅如此,在本月最新的 TIOBE 排行榜中,Python 再次超越 C++,位居排行榜前三甲,其受歡迎程度不言而喻,但就在此時(shí),Python 卻慘遭開發(fā)者嫌棄了,而這究竟是怎么一回事?
以下為譯文:
有時(shí)候我會(huì)跟一些搞技術(shù)的朋友們出去吃飯,當(dāng)然我們會(huì)愉快地討論技術(shù)。我們討論自己項(xiàng)目、討論新鮮事、討論各種技術(shù)問題。最終不可避免地會(huì)討論到編程語言。有人就會(huì)說“我又得改 Java 代碼了。我恨 Java?!保ū噶?Kyle……)(其實(shí)十多年前我們就給 Kyle 起了個(gè)綽號(hào)“Java 小子”。)另一個(gè)人就會(huì)談起某個(gè)古老的無所不包的 Shell 代碼,那段代碼無人敢碰。
至于我,嗯……我的話一出口眾人都驚呆了:我討厭 Python。我討厭它是有原因的。如果我能用 C 重寫某段 Python 代碼,那我一定會(huì)重寫的。
但我吼完之后,Bill 幽默地補(bǔ)刀:“那你內(nèi)心里是怎么看 Python 的,Neal?”所以就有了這篇文章。謹(jǐn)以此文獻(xiàn)給 Bill。
下面是我列出的“8 個(gè)理由說明 Python 很糟糕”。
版本
默認(rèn)的 Linux 安裝很可能會(huì)帶有多個(gè)版本的 Python。很可能會(huì)同時(shí)擁有 Python 2 和 Python 3,而且很可能同時(shí)擁有不同的子版本,如 3.5 和 3.7。理由是:Python 3 不能與 Python 2 完全兼容。即使一些子版本號(hào)也會(huì)造成無法后向兼容。
我不反對(duì)給語言添加新功能,甚至退役一些舊版本也無所謂。但是,不同的軟件需要不同的 Python。我給 Python 3.5 編寫的代碼不能在 Python 3.7 上正常運(yùn)行,除非我專門將其移植到 3.7。許多 Linux 開發(fā)者都認(rèn)為移植不值得,所以 Ubuntu 就同時(shí)帶有 Python 2 和 Python 3,因?yàn)椴煌暮诵墓δ苄枰煌?Python 版本。
缺乏向后兼容和版本之間的割裂通常是死亡的信號(hào)。Commodore 是世界上最早制造家用電腦的廠商之一(遠(yuǎn)在 IBM PC 和蘋果之前)。但 Commodore 的 PET 沒法與后繼的 Commodore CBM 電腦兼容。而且,CBM 也不兼容 VIC-20、Commodore-64、Amiga 等。所以,你只能花大把時(shí)間把代碼從一個(gè)平臺(tái)移植到另一個(gè)平臺(tái),否則就要完全放棄那個(gè)平臺(tái)。(現(xiàn)在 Commodore 在哪兒?它早就因?yàn)橛脩舴艞壦钠脚_(tái)而死了。)
同樣命運(yùn)的還有 Perl。Perl 曾經(jīng)非常流行。但 Perl 3 問世時(shí),它與很多 Perl 2 代碼都不兼容。社區(qū)對(duì)此意見很大,只有好的代碼得到了移植,其他代碼都被拋棄了。然后 Perl 4 出現(xiàn)時(shí)又發(fā)生了同樣的事情。Perl 5 出現(xiàn)時(shí),很多人干脆換到了其他更穩(wěn)定的編程語言?,F(xiàn)在,只有很少一部分人仍然在使用 Perl 來維護(hù)現(xiàn)有的 Perl 項(xiàng)目。已經(jīng)沒有任何新項(xiàng)目使用 Perl 了。
同樣,Python 的每個(gè)版本的代碼也都是不一樣的,社區(qū)也不得不維護(hù)舊的版本。因此就要不斷維護(hù)一大堆陳舊已失去活力的 Python 代碼,因?yàn)闆]人想把它們移植到新版本。據(jù)我所知,現(xiàn)在沒有人在 Python 2 上寫新代碼,但現(xiàn)有的 Python 2 又不得不維護(hù),因?yàn)槿藗儾辉敢鈱⑺鼈円浦驳?Python 3。在 Python 的官方網(wǎng)站上,Python 2.7、3.5、3.6 和 3.7 的文檔都在維護(hù)中,因?yàn)檫€有舊代碼在使用這些版本,他們沒辦法放棄這些版本。Python 就像編程語言中的百足之蟲,死而不僵。
安裝
絕大多數(shù)軟件包都可以通過 apt、yum、rpm 或某種安裝方式獲得最新的代碼。而 Python 則不一樣。你無法知道 apt-get install python 會(huì)給你裝什么版本,很可能這個(gè)版本跟你的代碼都不兼容。
所以,必須選擇你所需版本的 Python 來安裝。我參與過的一個(gè)項(xiàng)目使用的是 Python,但必須使用 Python 3.5(當(dāng)時(shí)的最新版本)。最后我的電腦上安裝了 Python 2、 Python 2.6、Python 3 和 Python 3.5。兩個(gè)是操作系統(tǒng)自帶的,一個(gè)是為項(xiàng)目安裝的,另一個(gè)來自我安裝的某個(gè)不相關(guān)的軟件。雖然它們都是 Python,但并不是完全一樣。
要想給 Python 安裝軟件包,應(yīng)該使用“pip”命令。(pip 的意思是“PIP Install Packages”,因?yàn)橛腥擞X得遞歸的縮寫很有意思。)但由于系統(tǒng)上有多個(gè)版本的 Python,你必須要注意使用正確版本的 pip。否則,pip 可能運(yùn)行的是 pip 2 而不是你需要的“pip 3.7”。(如果 pip 3.7 這個(gè)命令不存在,你還得指定正確的路徑。)
有個(gè)團(tuán)隊(duì)成員建議我,我應(yīng)該配置自己的環(huán)境,這樣一切都能使用 Python 3.5 的版本。這種方法很好,但后來我的另一個(gè)項(xiàng)目需要 Python 3.6 就出現(xiàn)麻煩了。兩個(gè)并行的項(xiàng)目使用了不同版本的 Python……嗯,這還不夠迷惑。(表示諷刺的表情是什么來著?)
pip 安裝程序會(huì)把文件放到用戶的本地目錄中。系統(tǒng)范圍上的軟件包不能使用 pip 安裝。而且你也不能使用 sudo pip,因?yàn)槟菚?huì)搞亂你整個(gè)電腦!因?yàn)槭褂?sudo 會(huì)在整個(gè)系統(tǒng)級(jí)別安裝軟件,一些軟件會(huì)安裝到錯(cuò)誤的 Python 版本,一些會(huì)留在你的主目錄中但卻屬于 root,導(dǎo)致以后的非 sudo pip 命令由于權(quán)限問題而出錯(cuò)。所以不要使用 sudo pip。
另外,誰負(fù)責(zé)維護(hù)這些 pip 模塊?是社區(qū)。也就是說,沒有固定的擁有者,也沒有強(qiáng)制的保證或?qū)徲?jì)。今年早些時(shí)候,某個(gè)版本的 PyPI 被發(fā)現(xiàn)有個(gè)后門,會(huì)盜取 SSH 密碼。這種事情根本不奇怪。(同樣的原因我也不用 Node.js 和 npm,我不相信他們的社區(qū)軟件倉(cāng)庫(kù)。)
語法
我極其推崇代碼的可讀性。初看起來,Python 代碼似乎可讀性很高。沒錯(cuò),不過條件是你不要用它來開發(fā)大型代碼。
絕大多數(shù)編程語言都有某種標(biāo)識(shí)來表明作用域——即函數(shù)何時(shí)開始何時(shí)結(jié)束,動(dòng)作包含在一個(gè)條件語句中,變量定義的范圍,等等。不論是 C、Java、JavaScript、Perl 還是 PIP,大家都使用{ ... } 為復(fù)雜的代碼定義作用域,而 Lisp 使用(...)定義作用域。Python 呢?Python 使用空格。如果需要為一段復(fù)雜的代碼定義作用域,就必須要縮進(jìn)接下來的幾行??s進(jìn)結(jié)束就表明作用域的結(jié)束。
我第一次看到 Python 代碼時(shí),我認(rèn)為使用縮進(jìn)定義作用域是個(gè)不錯(cuò)的想法。但是,這種方式有個(gè)巨大的缺點(diǎn)。這種方式可以寫出很深的嵌套,但代碼行也會(huì)變得很寬,導(dǎo)致在文本編輯器中折行。長(zhǎng)的函數(shù)和長(zhǎng)的條件動(dòng)作很難找出作用域的開始和結(jié)束。而且,只要你數(shù)錯(cuò)了空格,或者在某行開頭放了三個(gè)空格而不是四個(gè)空格,那你需要花上幾個(gè)小時(shí)的調(diào)試才能找到問題所在。
在其他語言中書寫調(diào)試代碼時(shí),我習(xí)慣不放任何縮進(jìn)。這樣我就能迅速瀏覽到代碼,并在調(diào)試結(jié)束之后很容易地找到調(diào)試代碼并刪掉。但用 Python 呢?任何縮進(jìn)不正確的行都會(huì)導(dǎo)致縮進(jìn)錯(cuò)誤。也就是說,調(diào)試代碼必須混合到正式代碼中。
包含
大多數(shù)編程語言都有一些方法可以包含其他代碼塊。C 語言有“#include”。PHP有'include','include_once','require'和'require_once'。而 Python 有“import”。
Python 的導(dǎo)入允許包括整個(gè)模塊,模塊的一部分或模塊中的特定功能。想知道哪些東西可以導(dǎo)入,并沒有什么直觀的辦法。使用 C,你可以查看 /usr/include/*.h。但是用 Python?最好使用 'python -v' 列出它看起來的所有位置,然后搜索該列表中每個(gè)目錄和子目錄中的每個(gè)文件。我曾經(jīng)看到我喜歡 Python 的朋友 grep 標(biāo)準(zhǔn)模塊來尋找他們想要導(dǎo)入的東西。這是真事。
導(dǎo)入功能還允許用戶重命名導(dǎo)入的代碼。它們基本上定義了一個(gè)自定義命名空間。乍一看,這似乎很不錯(cuò),但最終會(huì)影響可讀性和長(zhǎng)期支持。重命名模塊非常適合小腳本,但對(duì)于長(zhǎng)程序來說真的很糟糕。使用 1-2 字母命名空間的人,例如“import numpy as n”應(yīng)該拖出去槍斃(或強(qiáng)制將其所有代碼轉(zhuǎn)換為 Perl 5)。
但這不是最糟糕的部分。對(duì)于大多數(shù)語言,include 一段代碼只會(huì)包含代碼。一些語言(如面向?qū)ο蟮?C ++)會(huì)在存在全局構(gòu)造函數(shù)的情況下執(zhí)行代碼。類似地,一些 PHP 代碼可能會(huì)定義全局變量,因此導(dǎo)入可以運(yùn)行代碼——但通常人們認(rèn)為這不是一種好做法。相比之下,許多 Python 模塊包括在導(dǎo)入期間運(yùn)行的初始化函數(shù)。你不知道哪部分代碼在運(yùn)行,你不知道它在做什么,你甚至可能都注意不到。哦,有一種情況你會(huì)注意到——那就是出現(xiàn)命名空間沖突的時(shí)候,在這種情況下,你需要花很多時(shí)間來追蹤原因。
命名法
在其他所有語言中,數(shù)組都稱為“array”。在 Python 中,它們被稱為“l(fā)ist”。關(guān)聯(lián)數(shù)組有時(shí)稱為'hash'(Perl),但 Python 稱之為'dict'。 Python 似乎沒有使用在計(jì)算機(jī)和信息科學(xué)領(lǐng)域的常用術(shù)語。
然后是庫(kù)的名稱。 PyPy,PyPi,NumPy,SciPy,SymPy,PyGtk,Pyglet,PyGame ...(是的,第一個(gè)和第二個(gè)的讀音相同,但是它們是完全不同的東西。)我知道'py'表示 Python。但 py 放在開頭還是結(jié)尾能不能有個(gè)固定的寫法呢?
一些常見的庫(kù)只是放棄了類似雙關(guān)語的“Py”命名約定。這包括 matplotlib,nose,Pillow 和 SQLAlchemy。雖然一些名稱可能暗示庫(kù)的目的(例如,“SQLAlchemy”包含 SQL,所以它可能是一個(gè) SQL 接口),但其他名稱只是隨機(jī)的單詞。如果你不知道“BeautifulSoup”是干什么的,你能從名稱中看出它是一個(gè) HTML / XML 解析器嗎? (順便說一句,BeautifulSoup 有很好的文檔和易于使用。如果每個(gè) Python 模塊都是這樣的,我不會(huì)抱怨太多。不幸的是,這并不是常態(tài)。大多數(shù) Python 庫(kù)的文檔寫得都很差。 )
總的來說,我將 Python 視為具有可怕且不一致的命名約定的庫(kù)的集合。我有一個(gè)常見的抱怨,開源項(xiàng)目通常有可怕的名字。除非你知道這個(gè)項(xiàng)目,否則你永遠(yuǎn)不知道它的名字是什么。除非你知道要尋找什么,否則只能期待于偶然遇到某個(gè)別人提起的庫(kù)了。而且大多數(shù) Python 庫(kù)都強(qiáng)化了這種負(fù)面的批評(píng)。
怪癖
每種語言都有它的怪癖。C 語言使用&和*來訪問地址空間和值的做法很奇怪。 C 還有使用 ++ 和 -- 來表示遞增/遞減的快捷方式。在 Bash 中,當(dāng)引用括號(hào)和正則表達(dá)式的句點(diǎn)等特殊字符時(shí),就會(huì)出現(xiàn)“何時(shí)使用反斜杠”的問題。 JavaScript 存在兼容性問題(并非每個(gè)瀏覽器都支持所有有用的功能)。然而,Python 比我見過的任何其他語言都有更多怪癖。就拿字符串來說:
C 語言中雙引號(hào)表示字符串,單引號(hào)表示字符。
PHP 和 Bash 中,任何引號(hào)都可以用來表示字符串。但是,雙引號(hào)可以在字符串中嵌入變量。與此相反,單引號(hào)字符串是單純的字符串,任何類似嵌入式變量的名稱都不會(huì)擴(kuò)展。
在 JavaScript 中,單引號(hào)和雙引號(hào)之間沒有任何區(qū)別。
Python 中的單引號(hào)和雙引號(hào)之間也沒有區(qū)別。但是,如果您希望字符串跨越行,則需要使用三引號(hào)"""string"""或“''string'''。如果你想使用二進(jìn)制文件,那么你需要用 b(b'binary')或 r(r'raw')來指示字符串的形式。有時(shí)你需要使用 str(string) 將字符串轉(zhuǎn)換為字符串,或使用 string.encode('utf-8') 將其轉(zhuǎn)換為 utf8。
如果你認(rèn)為 =,== 和 === 在 PHP 和 JavaScript 中有點(diǎn)奇怪,那你應(yīng)該看看 Python 中的引號(hào)使用方法再下結(jié)論。
對(duì)象的引用傳遞
大多數(shù)編程語言都用值方式傳遞函數(shù)參數(shù)。如果函數(shù)改變了值,則改變不會(huì)影響到調(diào)用的代碼。但正如我已經(jīng)解釋過的那樣,Python 在這方面依然與眾不同。 Python 默認(rèn)使用引用方式傳遞參數(shù)。這意味著更改參數(shù)可能會(huì)導(dǎo)致原始值的改變。
這是過程式編程、函數(shù)式編程和面向?qū)ο缶幊陶Z言之間的重大差異之一。如果每個(gè)變量都是通過對(duì)象引用傳遞的,并且對(duì)變量的任何更改都會(huì)影響到任何使用該變量的地方,實(shí)際上就相當(dāng)于一切都使用了全局變量。針對(duì)一個(gè)變量使用不同的名稱實(shí)際上都是同一個(gè)對(duì)象,所以跟使用全局變量沒什么區(qū)別。C 程序員很久以前就知道,全局變量是邪惡的,不應(yīng)該被使用。
Python 中要想按值傳遞變量就必須使用額外的方式。簡(jiǎn)單地寫下“a = b”只會(huì)給同一個(gè)對(duì)象起另一個(gè)名字,而不會(huì)將 b 的值復(fù)制到 a 中。如果你確實(shí)要復(fù)制該值,則需要使用復(fù)制功能。通常是用“a = b.copy()”。但是請(qǐng)注意我說的是“通?!?。并非所有數(shù)據(jù)類型都支持“復(fù)制”的原型,還有可能復(fù)制功能不完整。在這些情況下,你必須使用一個(gè)名為“copy”的獨(dú)立庫(kù),即“a = copy.deepcopy(b)”。
局部命名
根據(jù)使用的庫(kù)或函數(shù)對(duì)程序進(jìn)行命名,是常見的編程技巧。例如,如果我要對(duì)使用某個(gè)名為"libscreencapture.so”的庫(kù)的截屏程序進(jìn)行測(cè)試,我會(huì)把我的程序叫做“screencapture.c",并編譯成“screencapture.exe”。
gcc-oscreencapture.exescreencapture.c-lscreencapture
這個(gè)技巧能在 C、Java、JavaScript、Perl、PHP 等語言中正確使用,因?yàn)檎Z言能區(qū)分出資源庫(kù)文件和本地的程序,因?yàn)樗鼈兊穆窂绞遣灰粯拥摹5?Python 卻不行。為什么?Python 認(rèn)為你想優(yōu)先導(dǎo)入本地文件。如果我有個(gè)程序叫做“screencapture.py”,它要執(zhí)行“import screencapture”,那么它將導(dǎo)入自己,而不是導(dǎo)入系統(tǒng)庫(kù)。至少,你得把本地的庫(kù)命名為“myscreencapture.py”之類的才行。
也并非一無是處
Python 是個(gè)非常流行的語言,有很多擁護(hù)者,我甚至有一堆朋友真的很喜歡 Python。多年以來,我一直與他們討論這些問題,每次他們都表示同意。他們同意這些都是 Python 的問題,只是他們覺得這些還不足以讓他們失去對(duì) Python 的興趣。
我的朋友經(jīng)常提起 Python 那些非常酷的函數(shù)庫(kù)。我也同意,某些函數(shù)庫(kù)非常有用。例如, BeautifulSoup 就是我用過的最好的 HTML 解析器之一,NumPy 簡(jiǎn)化了多維數(shù)組和復(fù)雜的數(shù)學(xué)計(jì)算,TensorFlow 在機(jī)器學(xué)習(xí)方面非常有用。但我不會(huì)只因?yàn)?TensorFlow 或者 SciPy 就用 Python 編寫一個(gè)大而全的程序。我不會(huì)放棄可讀性和可維護(hù)性,這樣做不值得。
通常,我在批評(píng)某個(gè)東西時(shí)也會(huì)寫一些正面的東西。比如,我的博客上“開源很糟糕”后面寫了一篇“開源很不錯(cuò)”,批評(píng)完 ffmpeg 的局限性之后也會(huì)特別提到它是最優(yōu)秀的視頻處理庫(kù)。但 Python 我寫不出優(yōu)點(diǎn)列表來,因?yàn)槲艺娴挠X得它太糟了。
-
編程語言
+關(guān)注
關(guān)注
10文章
1946瀏覽量
34792 -
python
+關(guān)注
關(guān)注
56文章
4797瀏覽量
84787
原文標(biāo)題:頻頻霸榜的Python,竟遭開發(fā)者嫌棄!
文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論