1. 程式人生 > >為什麼我覺得Python爛的要死?

為什麼我覺得Python爛的要死?

為什麼我覺得Python爛的要死?

 

 

https://www.toutiao.com/a6636558446030225923/

 

 

作為機器學習程式設計師的首選程式語言,Python成為世界範圍內最受大學生歡迎的程式語言。但凡事有例外,近日,一位開發者講述了他無法忍受Python的8大原因,引發網友大量回應。

 

作為機器學習程式設計師的首選程式語言,Python近年來可謂如日中天,人氣連年暴漲。由於AI熱潮持續不斷,Python在今年更是取代Java,成為世界範圍內最受大學生歡迎的程式語言。很多機器學習領域的教材、文章和技術文件,給出程式碼時會以Python作為示例語言,可見Python受歡迎程度之高。

 

Python具有上手快、門檻低、語法結構相對簡單等優點,初學者易入門、老手的二次學習成本也低,再加上機器學習任務上優勢獨具,受熱捧簡直是水到渠成的事。

 

但凡事有例外,東西再好也不可能人人都愛。

 

近日在hackerfactor上,一位名叫Neal Krawetz的人就撰文,指出了自己無法忍受Python的八大原因,把Python裡裡外外吐槽了一遍。

 

文章列出了作者認為Python存在重大缺陷的八條理由,包括版本相容性問題、安裝版本混亂、在程式關鍵字命名規則、常用庫命名規則上獨樹一幟,且缺乏一致性、賦值傳遞混亂、本地檔案命名策略易出錯等。

 

總之一通下來,把Python貶得夠嗆。這篇文章在當下Python大熱的背景下可算是一朵“奇葩“了。看多了Python讚歌是不是有點審美疲勞了?不妨換換口味。

 

原因1:版本之間不相容

安裝Linux後,那麼它很可能預設會安裝多個版本的Python,可能有Python2和Python3,甚至更多零零碎碎的版本,如3.5或3.7。

這是有原因的:Python3與Python2不完全相容,一些其他版本在這方面的缺陷也足夠明顯——向後相容性不足(backwards compatibility,也稱為向下相容性)。

 

所以Ubuntu同時安裝了Python2和Python3,因為這些版本的核心功能是不同的。

 

缺乏向後相容和分離版本通常是走向衰敗的預警。Commodore建立了第一臺家用電腦(要遠早於IBM PC或Apple之前)。但Commodore PET與隨後的Commodore CBM計算機並不相容,而CBM與VIC-20,Commodore-64,Amiga等也不相容。因此,使用者要麼花費大量時間將程式碼從一個平臺導到另一個平臺,要麼就直接放棄了這個平臺——Commodore就是前車之鑑。當用戶選擇放棄平臺時,它就註定會消失。

 

同樣,Perl曾經很受歡迎。但是當Perl3問世時,它並沒有完全向後相容Perl2的程式碼。接下來是Perl4。當Perl5問世時,很多人選擇轉向使用其他更穩定的程式語言。所以今天,只有一小部分人還在積極使用Perl來維護現有的Perl專案,而其他任何基於Perl的重大新專案再也沒有出現過。

同樣,Python為每個版本設計了不同的程式碼孤島。社群一直拖拽著這些舊版本,所以你最終也只能得到那些舊的、過時的Python程式碼,因為沒有人願意花時間將它導到最新版本上。

 

據我所知,沒有人在Python2上建立新的程式碼,但我們還讓它苟延殘喘著,因為沒人將所需的程式碼導到Python3.x. 在官方Python網站上,這些文件被主動維護並可用於Python 2.7、3.5、3.6和3.7——因為他們無法放棄舊程式碼。Python就像程式語言的殭屍——行屍走肉般向前走。

 

原因2:安裝太太太太麻煩了

通常來說,你直接apt、yum、rpm後得到就是最新穩定版。

但你如果'apt-get install python',就不知道是什麼版本,可能與你需要的所有程式碼都不相容。所以你在安裝的時候需要指定Python版本。

 

有一個專案需要用Python3.5(當時最新的版本),然而我的電腦上最終安裝了一大堆版本:Python2、Python2.6、Python3和Python3.5。兩個來自作業系統,一個為了專案安裝,一個是因為我安裝了一些不相關的軟體。

 

儘管它們都是“Python”,但它們並非完全相同。

 

如果你想安裝Python的軟體包,你應該使用“pip”(Pip代表“Pip Installs Packages”)。但是由於系統上有許多版本的Python,你必須記住使用正確版本的pip。否則,'pip'可能會執行'pip2'而不是你需要的'pip3.7'。(如果名稱不存在,你需要指定pip3.7的實際安裝路徑。)

 

我被一個朋友告知我需要配置環境,以便所有東西都能使用Python 3.5。這種方法的確很有效,但沒有持續多久,因為我開始了另一個需要Python 3.6版本的專案。兩個併發專案有兩個不同版本的Python——emmmm,這有點一言難盡吧。

 

pip安裝程式將檔案放在使用者的本地目錄中。你不能使用pip來安裝系統範圍的庫,並且Gawd會阻止你犯下執行'sudo pip'的錯誤,因為這會搞砸整個電腦!

 

順便說一句,是誰維護這些pip模組?答案是社群。也就是說,沒有明確的所有者,也沒有強制性的責任所屬。今年早些時候,一個版本的PyPi有一個後門發生了SSH憑據盜竊,但我對此一點都不驚訝,因為社群儲存庫根本不值得相信。出於同樣的原因,我也不使用Node.js和npm。

 

原因3:令人頭疼的語法問題,作用域使用空格導致可讀性差

 

我是可讀程式碼的堅定信徒。乍一看,Python似乎非常易讀,而當你開始製作大型程式碼庫,這種易讀性就會減弱了。

 

其他程式語言,像C, Java, JavaScript, Perl, and PHP,用{} 來表示作用域;List用()。Python用空格。如果你需要給一個複雜的程式碼定義一個作用域,然後你縮進了下面幾行程式碼,當縮排終止後,作用域就終止了。

 

Python手冊說你可以使用任意數量的空格或製表符來定義範圍。但是,每次縮排都要用四個空格!如果要縮排兩次以進行巢狀,那就得使用八個空格!

 

Python社群已經對這個術語進行標準化,儘管它沒出現在Python手冊中。文件中的示例說可以使用TAB、“TAB+1空格”等等。但是社群卻對4個空格有著喪心病狂的偏執!因此,除非你打算永遠不向其他任何人展示你寫的程式碼,否則每個縮排都要使用四個空格。

 

當我第一次看到Python程式碼時,我認為使用縮排來定義範圍似乎是個好主意。事實上,我太天真了,這簡直是一個天大的缺點。

 

深度巢狀是可以進行的,但每行程式碼會變得很寬,不得不在文字編輯器中換行。長函式和長條件操作都可能讓開始與結束變得難以匹配。我可憐那些錯誤計算空格數量(比如只輸了3個空格而不是四個)的人,因為這樣的錯誤需要數小時進行除錯和追蹤。

 

我debug程式碼習慣沒有縮排,這樣我就可以快速瀏覽程式碼,並在完成後輕鬆識別和刪除debug程式碼。

 

但是用Python呢?縮排錯誤的話,都會報錯。

 

原因4:特立獨行的載入庫方式

大多數程式語言都有一些方法可以包含其他程式碼塊。對於C,它是“#include”。對於PHP,有'include','include_once','require'和'require_once'。而對於Python,則是“import”。

 

Python的import允許匯入整個模組、模組的一部分或模組中的特定功能。但查詢匯入程式碼塊的方法卻很麻煩。使用C,直接看/usr/include/*.h就行了。但用Python?最好使用'python -v'列出所有位置,然後搜尋該列表中每個目錄和子目錄中的每個檔案。這真的很麻煩。

 

匯入功能還允許使用者重新命名匯入的程式碼,它們基本上定義了一個名稱空間。乍一看,這似乎很不錯,但這最終會影響可讀性和長期支援。重新命名模組非常適合小指令碼,但對於大程式來說真的很糟糕。這樣的操作“import numpy as n”,應該被打死。

 

但這不是最糟糕的部分。對於大多數語言,包含程式碼真的只意味著包含程式碼。而一些語言(如面向物件的C ++)則可以執行程式碼。類似地,一些PHP程式碼可能會定義全域性變數,因此一項import可以執行程式碼,但這通常被認為是一種不好的做法。相比之下,許多Python模組包含在匯入期間執行的初始化函式。你不知道什麼在執行,你不知道它在做什麼,你甚至都沒察覺到。除非存在名稱空間衝突,否則在這種情況下,你需要花很長時間來查詢原因。

 

原因5:關鍵字和庫命名“獨樹一幟”

在其他所有程式語言中,陣列都稱為“array”。在Python中,陣列被稱為“list”。在其他語言中,關聯陣列有時稱為'hash'(Perl),但Python裡叫做“dictionary”。 Python似乎沒有使用在計算機和資訊科學領域的常用術語。

 

然後是庫的名稱。看看這些名字吧,PyPy、PyPi、NumPy、SciPy,SymPy、PyGtk、Pyglet,PyGame ...(是的,前兩個名稱發音一模一樣,但是它們的功能和用途有很大區別。)我知道“py”代表Python。但這兩個字母就不能固定在庫的開頭或是末尾嗎?

 

而且一些常見的庫並沒有沿用這個所謂的“Py”命名約定。比如matplotlib、nose、Pillow和SQLAlchemy。雖然從一些命名上能夠看出庫的一些功能(比如“SQLAlchemy”包含SQL,所以它可能是一個SQL介面),但很多名稱只是隨機化的單詞。如果你事先並不知道“BeautifulSoup”是幹什麼用的,你能從名稱中看出它是一個HTML / XML解析器嗎?(順便說一句,BeautifulSoup庫的說明文件很完備,非常易於使用。如果每個Python模組都這麼好用,我也不會在這裡吐槽這麼多。但遺憾的是,這只是個例外,而不是常態。大多數Python庫的文件都爛的要死。)

 

總的來說,我認為Python對庫的命名非常混亂,缺乏一致性的原則。我總覺得,開源專案的命名都存在這種規則混亂的問題。除非你瞭解這個專案,否則你從專案名字上根本看不出來。除非你知道要找的是什麼,否則你很可能永遠都無法找到想找的東西。從大多數Python庫的命名上看,我現在更加確信這個觀點了。

 

原因6:其他“獨樹一幟”之處略多

每種語言都有它的怪癖。在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中使用引號時再說吧。

 

原因7:賦值方式怪異

大多數程式語言都按值傳遞函式引數。如果函式改變了值,則結果不會傳遞迴呼叫程式碼。但Python不一樣。 Python預設使用pass-by-object-reference引數執行函式。這意味著更改源變數可能最終會改變值。

 

這是面向程式、面向函式和麵向物件程式語言之間的重大差異之一。如果每個變數都是通過物件引用傳遞的,而且對變數的任何更改都會導致其他所有地方的變數值變化,那麼其實也可以全部使用全域性變數來處理所有內容。使用不同的名稱呼叫同一個物件不會更改物件的值,因此實際上該物件就是全域性的。C語言程式設計師有句老話,全域性變數是邪惡的,不應該使用。

 

在Python中,必須按值傳遞變數。“a = b”只是為同一個物件空間指定另一個名稱,並不會將b的值賦到a中。如果要賦值,則需要使用copy函式。通常格式是“a = b.copy()”。但是,請注意我說的是“通常”。並非所有資料型別都能夠這樣賦值,部分功能可能不完整。這時需要使用一個名為“copy”的獨立庫:“a = copy.deepcopy(b)”。

 

原因8:本地程式命名易混亂

根據使用的庫或函式來命名程式是一種常見的程式設計技術。比如,我正在使用名為“libscreencapture.so”的C語言庫測試螢幕捕獲程式,我呼叫的程式可能會命名為“screencapture.c”,編譯後命名為“screencapture.exe”。

 

如果使用C,Java,JavaScript,Perl,PHP等語言,這種命名方式很好用,因為程式語言可以很容易地將資源庫與本地程式區分開來,因為彼此的路徑是不同的。但是如果用的是Python,永遠不要這樣命名。

 

為什麼? Python總是假定使用者首先要匯入原生代碼。如果我有一個名為“screencapture.py”的程式使用“importscreencapture”,那麼它將匯入自己而不是系統庫。至少,本地程式需要命名為“myscreencapture.py”才能避免這種錯誤。

 

 

當然了,吐槽了這麼多,但其實Python並非一無是處。

Python是一種非常流行的語言,擁有數量龐大的使用者。我身邊有一些朋友非常喜歡Python,這是他們首選的程式語言。多年來,我和他們討論過這些問題,每次他們都點頭表示同意。他們並不否認Python確實存在這些問題,只是覺得這些缺點不足以讓他們拋棄Python。

 

我的朋友經常在程式設計中將所有存在的非常酷的Python庫統統引用。我也認為一些庫確實非常有用。例如,BeautifulSoup是我用過的最好的HTML解析器之一,NumPy使得多維陣列和複雜的數學過程更容易實現,而TensorFlow則對於機器學習非常有用。但是,我不會因為喜歡TensorFlow或SciPy,而在Python中建立一個單片程式。為了某些庫的便利性,放棄程式可讀性和可維護性,屬於得不償失。

 

一般來說,我在寫關於某個主題的負面批評文章時,也會嘗試寫一些正面的東西。比如當我寫FFmpeg的侷限性時,我也明確提到它是最好的視訊處理庫。但我這裡寫不出關於Python的什麼優點了,因為我真的覺得Python很爛。

 

此文一發,在評論區引發了激烈的爭論:

“是你不懂Python”

Mario Abarca

你的這些問題可以總結為一點:你不喜歡Python因為它和C風格不一樣

  1. 版本不相容不是bug,是特性;我就覺得沒人維護的東西就不應該再用了
  2. 用虛擬環境安裝不同的python版本而不是安裝在同一個環境下
  3. 現代編輯器預設TAB=4個空格。你也不需要非得用4個空格,但要確保一致性
  4. 官方的文件特別好,真的。要是標準庫裡沒有,翻翻The Hitchhiker’s Guide to Python這本書
  5. 我覺得Python的命名風格特別好,更直觀。list不是陣列,就是序列;關聯陣列明明就是dict
  6. 二進位制字串前面加個b,是因為Unicode規範中,1位元組≠1byte
  7. 這樣做的好處是,我可以隨時隨地引用一個東西,而不需要每次都去複製貼上原來的名字
  8. 同上

 

notacoward

  1. 1和2是同一個問題,有關整個生態,跟語言本身無關。因為這類社群維護的專案都是不同的人花費寶貴的業餘時間去維護,每個人都有每個人的習慣和價值觀
  2. 這個只能說你自己太個性了。我們大家保持預設的統一風格,對於別人維護起來明顯更容易
  3. C/C++的include很難處理模組介面
  4. list和array不是一個東西。下一個
  5. 每個語言都有自己的一套轉換方式。Python可能不是最完美的,但是其他的更差,呵呵
  6. 物件引用效率更高。尤其是當變數名不一致的時候,你直接複製會有問題。但是你引用一下,就好多了
  7. 最好不要把自己的程式命名成標準庫裡的程式或者模組的名字

 

folkrav

首先要糾正下你,PyPy和PyPi發音不一樣。前一個是“派派”,後一個是“派-屁-愛”

其次,名稱很重要嗎?第三方誒大哥,啥名字都可以出現誒大哥。你就能保證你起名的時候,能做到信達雅嗎大哥?

 

jaxtellerSoA

我就不明白了。用縮排來定義作用域,怎麼就不好了?多一目瞭然啊!別的語言{}裡面不也得縮排嗎?再說了,你就不覺得按住shift才能打出{}很難受嗎?

 

riskable

我跟C粉兒討論過“縮排vs括號”這個問題。他說沒有括號怎麼能輕鬆找出作用域呢?

標準Py粉兒答案是:啊原來你們喜歡括號是因為你們的程式碼壞習慣啊。

我想了想,可能這麼問更恰當:假如不使用文字編輯器/IDE來突出顯示括號或它們之間的空間,你還是堅持用括號不用縮排嗎?

我估計他終於get到我的點了,說:啊我明白了,你之所以用縮排是因為Python編輯器太爛了啊!真可憐。

 

Sign。

“我也不喜歡Python”

 

cutety

Python是我上手的第一個語言,但我以後再也不會用了。當然作者的這些問題,在我看來都不是問題,個人習慣而已。

  1. 包管理模式簡直爛到家。那麼多包管理器可以借鑑啊,可以讓pip不那麼爛啊
  2. 就不能有個標準包管理器有個標準manifest嗎?又不會懷孕!

 

twunde

安裝確實是個讓人頭疼的問題。是的很多人提到了安裝虛擬環境,venv/virtualenv。Ruby有RVM,可以輕鬆的在同樣環境下使用不同版本。我寧願挨個給Ruby,PHP,Perl…做環境配置,也不願意給Python配置。

 

nicoburns

哦!多行Lambda!我在JS裡的最愛。Python裡,沒!有!了!

 

setpatchaddress

我從1.5就開始用Python了。縮排來表示語句塊,是我最最最不能忍受的!

 

dbcurtis

我就喜歡C那種的括號,不喜歡Python的縮排方式。

 

colanderman

我覺得Python最大的問題其實是內部模型對於它的意圖而言過於複雜了,就是一個有經驗的開發者都很難理解,別說初學者了。