尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️
在剛剛過去的 2018 年里,要說最熱門的科技領域是哪一個?毋庸置疑的是,人工智能必排在前列;而要論編程語言界,最流行的編程語言是誰?那非 Python 莫屬。2018 年 8 月,根據一年一度的IEEE Spectrum 編程語言來看,Python 一路高歌猛進,位居 48 種編程語言之首。不僅如此,在本月最新的 TIOBE 排行榜中,Python 再次超越 C++,位居排行榜前三甲,其受歡迎程度不言而喻,但就在此時,Python 卻慘遭開發者嫌棄了,而這究竟是怎麼一回事?
作者 |Neal Krawetz譯者 |彎月責編 | 屠敏出品 | CSDN(ID:CSDNnews)
以下為譯文:
有時候我會跟一些搞技術的朋友們出去吃飯,當然我們會愉快地討論技術。我們討論自己項目、討論新鮮事、討論各種技術問題。最終不可避免地會討論到編程語言。有人就會說「我又得改 Java 代碼了。我恨 Java。」(抱歉了 Kyle……)(其實十多年前我們就給 Kyle 起了個綽號「Java 小子」。)另一個人就會談起某個古老的無所不包的 Shell 代碼,那段代碼無人敢碰。
至於我,嗯……我的話一出口眾人都驚呆了:我討厭 Python。我討厭它是有原因的。如果我能用 C 重寫某段 Python 代碼,那我一定會重寫的。
但我吼完之後,Bill 幽默地補刀:「那你內心里是怎麼看 Python 的,Neal?」所以就有了這篇文章。謹以此文獻給 Bill。
下面是我列出的「8 個理由說明 Python 很糟糕」。
版本
默認的 Linux 安裝很可能會帶有多個版本的 Python。很可能會同時擁有 Python 2 和 Python 3,而且很可能同時擁有不同的子版本,如 3.5 和 3.7。理由是:Python 3 不能與 Python 2 完全兼容。即使一些子版本號也會造成無法後向兼容。
我不反對給語言添加新功能,甚至退役一些舊版本也無所謂。但是,不同的軟件需要不同的 Python。我給 Python 3.5 編寫的代碼不能在 Python 3.7 上正常運行,除非我專門將其移植到 3.7。許多 Linux 開發者都認為移植不值得,所以 Ubuntu 就同時帶有 Python 2 和 Python 3,因為不同的核心功能需要不同的 Python 版本。
缺乏向後兼容和版本之間的割裂通常是死亡的信號。Commodore 是世界上最早製造家用電腦的廠商之一(遠在 IBM PC 和蘋果之前)。但 Commodore 的 PET 沒法與後繼的 Commodore CBM 電腦兼容。而且,CBM 也不兼容 VIC-20、Commodore-64、Amiga 等。所以,你只能花大把時間把代碼從一個平台移植到另一個平台,否則就要完全放棄那個平台。(現在 Commodore 在哪兒?它早就因為用戶放棄它的平台而死了。)
同樣命運的還有 Perl。Perl 曾經非常流行。但 Perl 3 問世時,它與很多 Perl 2 代碼都不兼容。社區對此意見很大,只有好的代碼得到了移植,其他代碼都被拋棄了。然後 Perl 4 出現時又發生了同樣的事情。Perl 5 出現時,很多人乾脆換到了其他更穩定的編程語言。現在,只有很少一部分人仍然在使用 Perl 來維護現有的 Perl 項目。已經沒有任何新項目使用 Perl 了。
同樣,Python 的每個版本的代碼也都是不一樣的,社區也不得不維護舊的版本。因此就要不斷維護一大堆陳舊已失去活力的 Python 代碼,因為沒人想把它們移植到新版本。據我所知,現在沒有人在 Python 2 上寫新代碼,但現有的 Python 2 又不得不維護,因為人們不願意將它們移植到 Python 3。在 Python 的官方網站上,Python 2.7、3.5、3.6 和 3.7 的文檔都在維護中,因為還有舊代碼在使用這些版本,他們沒辦法放棄這些版本。Python 就像編程語言中的百足之蟲,死而不僵。
安裝
絕大多數軟件包都可以通過 apt、yum、rpm 或某種安裝方式獲得最新的代碼。而 Python 則不一樣。你無法知道 apt-get install python 會給你裝什麼版本,很可能這個版本跟你的代碼都不兼容。
所以,必須選擇你所需版本的 Python 來安裝。我參與過的一個項目使用的是 Python,但必須使用 Python 3.5(當時的最新版本)。最後我的電腦上安裝了 Python 2、 Python 2.6、Python 3 和 Python 3.5。兩個是操作系統自帶的,一個是為項目安裝的,另一個來自我安裝的某個不相關的軟件。雖然它們都是 Python,但並不是完全一樣。
要想給 Python 安裝軟件包,應該使用「pip」命令。(pip 的意思是「PIP Install Packages」,因為有人覺得遞歸的縮寫很有意思。)但由於系統上有多個版本的 Python,你必須要注意使用正確版本的 pip。否則,pip 可能運行的是 pip 2 而不是你需要的「pip 3.7」。(如果 pip 3.7 這個命令不存在,你還得指定正確的路徑。)
有個團隊成員建議我,我應該配置自己的環境,這樣一切都能使用 Python 3.5 的版本。這種方法很好,但後來我的另一個項目需要 Python 3.6 就出現麻煩了。兩個並行的項目使用了不同版本的 Python……嗯,這還不夠迷惑。(表示諷刺的表情是什麼來著?)
pip 安裝程序會把文件放到用戶的本地目錄中。系統範圍上的軟件包不能使用 pip 安裝。而且你也不能使用 sudo pip,因為那會搞亂你整個電腦!因為使用 sudo 會在整個系統級別安裝軟件,一些軟件會安裝到錯誤的 Python 版本,一些會留在你的主目錄中但卻屬於 root,導致以後的非 sudo pip 命令由於權限問題而出錯。所以不要使用 sudo pip。
另外,誰負責維護這些 pip 模塊?是社區。也就是說,沒有固定的擁有者,也沒有強制的保證或審計。今年早些時候,某個版本的 PyPI 被發現有個後門,會盜取 SSH 密碼。這種事情根本不奇怪。(同樣的原因我也不用 Node.js 和 npm,我不相信他們的社區軟件倉庫。)
語法
我極其推崇代碼的可讀性。初看起來,Python 代碼似乎可讀性很高。沒錯,不過條件是你不要用它來開發大型代碼。
絕大多數編程語言都有某種標識來表明作用域——即函數何時開始何時結束,動作包含在一個條件語句中,變量定義的範圍,等等。不論是 C、Java、JavaScript、Perl 還是 PIP,大家都使用{ … } 為複雜的代碼定義作用域,而 Lisp 使用(…)定義作用域。Python 呢?Python 使用空格。如果需要為一段複雜的代碼定義作用域,就必須要縮進接下來的幾行。縮進結束就表明作用域的結束。
我第一次看到 Python 代碼時,我認為使用縮進定義作用域是個不錯的想法。但是,這種方式有個巨大的缺點。這種方式可以寫出很深的嵌套,但代碼行也會變得很寬,導致在文本編輯器中折行。長的函數和長的條件動作很難找出作用域的開始和結束。而且,只要你數錯了空格,或者在某行開頭放了三個空格而不是四個空格,那你需要花上幾個小時的調試才能找到問題所在。
在其他語言中書寫調試代碼時,我習慣不放任何縮進。這樣我就能迅速瀏覽到代碼,並在調試結束之後很容易地找到調試代碼並刪掉。但用 Python 呢?任何縮進不正確的行都會導致縮進錯誤。也就是說,調試代碼必須混合到正式代碼中。
包含
大多數編程語言都有一些方法可以包含其他代碼塊。C 語言有「#include」。PHP有’include’,’include_once’,’require’和’require_once’。而 Python 有「import」。
Python 的導入允許包括整個模塊,模塊的一部分或模塊中的特定功能。想知道哪些東西可以導入,並沒有什麼直觀的辦法。使用 C,你可以查看 /usr/include/*.h。但是用 Python?最好使用 ‘python -v’ 列出它看起來的所有位置,然後搜尋該列表中每個目錄和子目錄中的每個文件。我曾經看到我喜歡 Python 的朋友 grep 標準模塊來尋找他們想要導入的東西。這是真事。
導入功能還允許用戶重命名導入的代碼。它們基本上定義了一個自定義命名空間。乍一看,這似乎很不錯,但最終會影響可讀性和長期支持。重命名模塊非常適合小腳本,但對於長程序來說真的很糟糕。使用 1-2 字母命名空間的人,例如「import numpy as n」應該拖出去槍斃(或強制將其所有代碼轉換為 Perl 5)。
但這不是最糟糕的部分。對於大多數語言,include 一段代碼只會包含代碼。一些語言(如面向對象的 C ++)會在存在全局構造函數的情況下執行代碼。類似地,一些 PHP 代碼可能會定義全局變量,因此導入可以運行代碼——但通常人們認為這不是一種好做法。相比之下,許多 Python 模塊包括在導入期間運行的初始化函數。你不知道哪部分代碼在運行,你不知道它在做什麼,你甚至可能都注意不到。哦,有一種情況你會注意到——那就是出現命名空間衝突的時候,在這種情況下,你需要花很多時間來追蹤原因。
命名法
在其他所有語言中,數組都稱為「array」。在 Python 中,它們被稱為「list」。關聯數組有時稱為’hash’(Perl),但 Python 稱之為’dict’。 Python 似乎沒有使用在計算機和信息科學領域的常用術語。
然後是庫的名稱。 PyPy,PyPi,NumPy,SciPy,SymPy,PyGtk,Pyglet,PyGame …(是的,第一個和第二個的讀音相同,但是它們是完全不同的東西。)我知道’py’表示 Python。但 py 放在開頭還是結尾能不能有個固定的寫法呢?
一些常見的庫只是放棄了類似雙關語的「Py」命名約定。這包括 matplotlib,nose,Pillow 和 SQLAlchemy。雖然一些名稱可能暗示庫的目的(例如,「SQLAlchemy」包含 SQL,所以它可能是一個 SQL 接口),但其他名稱只是隨機的單詞。如果你不知道「BeautifulSoup」是幹什麼的,你能從名稱中看出它是一個 HTML / XML 解析器嗎? (順便說一句,BeautifulSoup 有很好的文檔和易於使用。如果每個 Python 模塊都是這樣的,我不會抱怨太多。不幸的是,這並不是常態。大多數 Python 庫的文檔寫得都很差。 )
總的來說,我將 Python 視為具有可怕且不一致的命名約定的庫的集合。我有一個常見的抱怨,開源項目通常有可怕的名字。除非你知道這個項目,否則你永遠不知道它的名字是什麼。除非你知道要尋找什麼,否則只能期待於偶然遇到某個別人提起的庫了。而且大多數 Python 庫都強化了這種負面的批評。
怪癖
每種語言都有它的怪癖。C 語言使用&和*來訪問地址空間和值的做法很奇怪。 C 還有使用 ++ 和 — 來表示遞增/遞減的快捷方式。在 Bash 中,當引用括號和正則表達式的句點等特殊字符時,就會出現「何時使用反斜杠」的問題。 JavaScript 存在兼容性問題(並非每個瀏覽器都支持所有有用的功能)。然而,Python 比我見過的任何其他語言都有更多怪癖。就拿字符串來說:
-
C 語言中雙引號表示字符串,單引號表示字符。
-
PHP 和 Bash 中,任何引號都可以用來表示字符串。但是,雙引號可以在字符串中嵌入變量。與此相反,單引號字符串是單純的字符串,任何類似嵌入式變量的名稱都不會擴展。
-
在 JavaScript 中,單引號和雙引號之間沒有任何區別。
-
Python 中的單引號和雙引號之間也沒有區別。但是,如果您希望字符串跨越行,則需要使用三引號”””string”””或「”string”’。如果你想使用二進制文件,那麼你需要用 b(b’binary’)或 r(r’raw’)來指示字符串的形式。有時你需要使用 str(string) 將字符串轉換為字符串,或使用 string.encode(‘utf-8’) 將其轉換為 utf8。
如果你認為 =,== 和 === 在 PHP 和 JavaScript 中有點奇怪,那你應該看看 Python 中的引號使用方法再下結論。
對象的引用傳遞
大多數編程語言都用值方式傳遞函數參數。如果函數改變了值,則改變不會影響到調用的代碼。但正如我已經解釋過的那樣,Python 在這方面依然與眾不同。 Python 默認使用引用方式傳遞參數。這意味著更改參數可能會導致原始值的改變。
這是過程式編程、函數式編程和面向對象編程語言之間的重大差異之一。如果每個變量都是通過對象引用傳遞的,並且對變量的任何更改都會影響到任何使用該變量的地方,實際上就相當於一切都使用了全局變量。針對一個變量使用不同的名稱實際上都是同一個對象,所以跟使用全局變量沒什麼區別。C 工程師很久以前就知道,全局變量是邪惡的,不應該被使用。
Python 中要想按值傳遞變量就必須使用額外的方式。簡單地寫下「a = b」只會給同一個對象起另一個名字,而不會將 b 的值復制到 a 中。如果你確實要復制該值,則需要使用復制功能。通常是用「a = b.copy()」。但是請注意我說的是「通常」。並非所有數據類型都支持「復制」的原型,還有可能復制功能不完整。在這些情況下,你必須使用一個名為「copy」的獨立庫,即「a = copy.deepcopy(b)」。
局部命名
根據使用的庫或函數對程序進行命名,是常見的編程技巧。例如,如果我要對使用某個名為”libscreencapture.so」的庫的截屏程序進行測試,我會把我的程序叫做「screencapture.c”,並編譯成「screencapture.exe」。
這個技巧能在 C、Java、JavaScript、Perl、PHP 等語言中正確使用,因為語言能區分出資源庫文件和本地的程序,因為它們的路徑是不一樣的。但在 Python 卻不行。為什麼?Python 認為你想優先導入本地文件。如果我有個程序叫做「screencapture.py」,它要執行「import screencapture」,那麼它將導入自己,而不是導入系統庫。至少,你得把本地的庫命名為「myscreencapture.py」之類的才行。
也並非一無是處
Python 是個非常流行的語言,有很多擁護者,我甚至有一堆朋友真的很喜歡 Python。多年以來,我一直與他們討論這些問題,每次他們都表示同意。他們同意這些都是 Python 的問題,只是他們覺得這些還不足以讓他們失去對 Python 的興趣。
我的朋友經常提起 Python 那些非常酷的函數庫。我也同意,某些函數庫非常有用。例如, BeautifulSoup 就是我用過的最好的 HTML 解析器之一,NumPy 簡化了多維數組和複雜的數學計算,TensorFlow 在機器學習方面非常有用。但我不會只因為 TensorFlow 或者 SciPy 就用 Python 編寫一個大而全的程序。我不會放棄可讀性和可維護性,這樣做不值得。
通常,我在批評某個東西時也會寫一些正面的東西。比如,我的博客上「開源很糟糕」後面寫了一篇「開源很不錯」,批評完 ffmpeg 的局限性之後也會特別提到它是最優秀的視頻處理庫。但 Python 我寫不出優點列表來,因為我真的覺得它太糟了。
原文:
https://www.hackerfactor.com/blog/index.php?/archives/825-8-Reasons-Python-Sucks.html
作者:Neal Krawetz,在 Texas A&M University 獲得計算機科學的博士學位,創立了 HackerFactor Solutions,專注於非傳統的計算機法醫學、在線評測、網路和計算機安全等。
本文為 CSDN 翻譯,如需轉載,請註明來源出處。
福利
公眾號後台回復:2018Python,獲取2018Python開源項目Top100整理資料!或掃碼添加小助手微信,回復:1,入群獲取。
推薦閱讀: