首頁/ 遊戲/ 正文

如果你要開始一個大專案,不要使用 Python......

整理 | 張仕影  責編 | 鄭麗媛

出品 | CSDN(ID:CSDNnews)

在很大程度上,Python並不像你想象的那麼好。

在開發者的職業生涯中,有一個特定階段,開發者們會從為專案做貢獻到掌握自己的技巧。這個階段對有些人來說會來得早一些,有些則也會晚一些,而另一部分人則無法到達這個階段。

不過,大多數職業生涯較長的開發人員都經歷過這個階段。我將這稱之為自己構建的點。

如果你已經到達了那個階段,你面對的第一個問題是:它是如何運作的?使用者體驗如何?架構是怎樣的?資料如何流動?以及很多類似這樣的問題。

我在這裡就不為你回答這些這些問題的答案了。無論你開始哪一個專案,它們都需要根據專案來高度定製,並且每一個都應該至少有一篇獨立的文章來解答。

不過,我想回答其中一個問題:哪種語言最適合這個專案?

你可能認為這取決於專案的型別,的確,但是每種程式語言都有一些缺陷。實際上,Python 也有很多缺陷,尤其是當你試圖用它來構建一個大型程式的時候。

如果你要開始一個大專案,不要使用 Python......

變數宣告不存在,這是一個問題

Python 之禪表明:顯式優於隱式。但當涉及到變數宣告時,Python 中的隱式比顯式更常見。

(注:Python 之禪指的是 Tim Peters 編寫的關於 python 程式設計準則。)

我們先來看看下面這一小段的 C 程式碼:

char

notpython[

50

] =

“This isn‘t Python。”

在對比 Python 之前,我們先來深入研究以下這個問題。

“char” 是一個型別識別符號,表示之後的所有內容都與一個字串有關。“notpython” 是我給這個字串取的名字。[50] 表示C將為此保留50個字元的記憶體空間。不過,在本例中,我可以使用 19個 字元——每個字元一個,在末尾加上一個空字元\0,最後用一個分號巧妙地結束。

這種顯式宣告在 C 語言中是強制性的。如果你忽略它,編譯器則會罷工。

這種方式起初看起來既愚蠢又乏味,但是它的回報非常大。

兩週或兩年後讀 C 程式碼的你突然發現了一個你不知道的變數,那麼查一下宣告就可以。如果你給它起了一個有意義的名字,那這就會給你一個極大的提示:它是什麼,它在做什麼,以及哪裡需要它。

對比一下 Python,你幾乎可以隨時創造變數。但如果你沒有給它取一個有意義的名字,或者至少留下一個關於它的註釋,那麼將來可能會一團糟。

在 Python 中,除了深入研究程式碼,否則很難理解變數在做什麼。甚至如果你在變數中有一個拼寫錯誤,你可能會破壞整個程式碼——Python 並沒有像 C 語言那樣的保護宣告。

如果你要處理的是較小的專案,或者不是很複雜的專案,比如說幾千行程式碼,這就沒有什麼問題 ,但如果是更大的專案…那就糟糕了。

你可以在 Python 中做顯式變數宣告,但只有一些非常勤奮的程式設計師才會這麼做。當編譯器沒有問題時,很多程式設計師們往往會忘記這些額外的程式碼行。

編寫 Python 是很快的,對於一些小而簡單的專案來說,閱讀 Python 也是很快的。不過閱讀和維護大型 Python 專案時,你最好成為找描述性變數名和註釋所有程式碼的世界英雄,否則你就完了。

如果你要開始一個大專案,不要使用 Python......

模組,你屬於哪裡?

如果你認為事情不會變得更糟糕,那麼你就錯了。

變數從哪裡開始“存在”於程式碼中的問題不僅僅來自隱式宣告,還可能來自其他模組,它們通常是以 my_module。my_variable() 這樣的形式存在。如果你被這樣一個變數搞糊塗了,那麼當你檢查它出現在主檔案中的其他位置時,你依舊會感到困惑。

你還必須檢查程式碼中是否包含以下兩行程式碼之一:

import

my_module

from another_module

import

my_module

第二行的作用是告訴編譯器,你需要從一個包含更多內容的模組中獲得某個函式或變數。

這很煩人,因為有模組比你在 PyPI 上找到的更多,還可以在計算機上匯入任何其他 Python 檔案。所以快速搜尋你的函式和變數並非全然有益,甚至它可能會變得更糟糕。

模組可以依賴於其他模組。因此如果你不走運,你匯入了模組 A、 B 和 C,但是這些模組依賴於模組 E、 F 、G 和 H,而模組 E、 F、G 和 H 又依賴於模組 I、 J 和 K。突然之間,你需要管理的不僅是 3 個模組,而是 10 個模組。

更糟糕的是,有時候並不是這麼簡單的結構。比如說模組 B 和 C 也取決於模組 M 和 N ,而 J 也依賴於 M、C 和 H 依賴於 Q…後面不必多說,你懂的。

這是一個迷宮,也是一個由 Pythonians 創造且真實存在的依賴地獄。

迴圈依賴是迷宮中最醜陋的野獸。如果模組 A 依賴於模組 B,但模組 B 同時使用模組 A 的一部分…

在小專案中這並不是問題,但在大專案中……歡迎來到叢林。

如果你要開始一個大專案,不要使用 Python......

大量依賴衝突

我要抱怨的不僅僅是模組本身,還有它們的版本。

原則上, Python 有活躍的使用者群,很多模組也會定期更新,這是非常好的。只是有一個問題:並非所有版本的模組都能與其他模組始終保持相容。

比如說你使用模組 A 和 B,而兩者都依賴於模組 C。但是 A 需要3。2或者更高版本的 C, B 需要2。9或者更早的版本 C。

可你不在乎模組 C,你只想要 A 和 B。

世界上沒有任何工具可以幫助你解決這場衝突。幸運的話,你會找到一個和你遇到同樣問題的人寫的補丁。如果你不是那麼幸運,你將不得不編寫補丁。或者你用別的包。再或者,你可以重寫其中一個包(A 或者 B),然後在需要 C 的地方找到變通方法。

無論如何,你都需要額外的時間來解決問題。這就像在一個叢林裡,你需要耐心以及一些導航工具來讓自己找到出路。

拋開依賴衝突,也有一些不錯的工具。像“pip”,它就可以很容易地安裝軟體包。使用一個簡單的“requirements 。 txt”,你可以指定哪些包以及你希望使用哪些版本等等。虛擬環境將所有包放在一個地方,並與主要 Python 分開安裝。

對於更大更復雜的專案,還有“conda”、 YAML 檔案等等。但是你需要學習如何使用每一種工具,確保用最少的時間解決問題。

如果你要開始一個大專案,不要使用 Python......

不同的機器,不同的 Python

即使你已經解決了機器上的所有依賴問題,你的 Python 執行起來十分流暢,也不能保證它在其他機器上執行時依舊這麼流暢。像“pip”、“ requirements 。 txt ”這樣的工具和虛擬環境可以幫助你瀏覽輕度依賴地獄,但僅限於本地。

在每臺新機器上,你都需要檢查並重新安裝各個版本及要求。

唯一簡便的解決方案是 Jupyter notebooks。在 Jupyter notebooks 中你可以用任何你喜歡的版本寫東西。在 Jupyter 中,一切都執行在一個線上伺服器上,你可以將這些檔案傳送給任何人,他們同樣能夠使用它們。

不過這也有一個明顯的缺點:Jupyter 筆記本只有圖形介面。但是使用圖形介面,處理具有許多相互關聯檔案的大型專案是相當困難的。

也許這就是為什麼我很難在 Jupyter 筆記本上看到大專案的原因吧。

而其他語言只要有虛擬機器,問題就解決了。

如果你要開始一個大專案,不要使用 Python......

pip 之外的世界

假設你已經設法透過使用 Jython 或 PyPy 或其他解決方案,將你的專案移植到不同的機器上(雖然這些比虛擬機器處理起來要笨拙一些,但至少能奏效),接下來為了整合大型專案,你可能會整合 C 包、 Fortran 包等。這樣做有許多好處:C包可能不存在於 Python 中,而且通常更快。由於遺留原因,科學包往往只存在於 Fortran 中。

但實際上,在這過程中你必須使用像 ‘gcc’、‘gfortran’ 這樣的編譯器,甚至其它更多編譯器——這很麻煩,因為在 Python 程式碼中整合 C 模組的文件超過 4500 字,整合 Fortran 的文件也不短。

所以如果一開始就用 C 構建整個專案可能會更好:雖然要慢一些,但是你可以避免必須使用多個編譯器和介面的情況。

C 很古老,幾乎任何東西都有包,甚至還有使用者友好的機器學習軟體包。

如果你要開始一個大專案,不要使用 Python......

使用全域性直譯器鎖鎖定效能

全域性直譯器鎖(GIL),從 Python 誕生的第一天起就已經存在,它使終端使用者的儲存管理變得非常容易。

至少在較小的專案中,開發人員在使用 Python 時根本不需要考慮計算機記憶體。相比之下,在C中每個變數都保留了記憶體位!

基本上,GIL 會計算一個變數在程式碼中每個部分被引用了多少次。如果不再需要該變數,則會釋放它所佔用的記憶體空間。因此在小型專案中,GIL 有助於提高效能,因為不必要的記憶體空間被清除掉了。但是在大專案中有一個問題:GIL 不喜歡多執行緒。

這是一種可以極大提高執行程式效能的方式,其中多個指令執行緒在相同的程序資源上獨立執行。機器學習模型非常適合以這種方式進行訓練。

只有一個小問題:GIL 一次只能在一個執行緒上工作。因此,如果變數 A 線上程 1 上執行,而執行緒 2 已經完成了變數A ,那麼它的記憶體可能會被刪除,這取決於當時 GIL 處在什麼位置。

正如你想象的那樣,這可能會導致非常奇怪的錯誤。

當然,這是有解決方法的,但它們都不是很完美,因為它通常不會像有 GIL 語言中的多執行緒那樣快。

如果你要開始一個大專案,不要使用 Python......

併發和並行仍然笨重和混亂

我們已經發現了併發的一個缺點,就是當你正在進行多執行緒時,全域性直譯器鎖會減慢速度,或者導致奇怪的錯誤。

同樣的缺點也存在於 Python 的協同程式。

執行緒和協程有一些細微的區別,即協程一次執行一個任務,而執行緒可以同時執行多個任務。相同點在於,它們都是併發的實現。

當你有大量需要等待的任務時,協程很有用,比如你正在讀取網站資料並等待伺服器響應。協程程式並不會讓計算機坐視不管,而是將另一個任務分配給它。另一方面,當你有幾個耗時的任務時,執行緒的優勢就體現出來了,不太佔用 CPU ,也不需要太多等待。

如果你有一個 CPU 密集型任務,並且想要充分利用你的硬體,那麼並行是值得考慮的。

多處理也是個不錯的選擇,它會告訴計算機使用多個核心,節省時間。

不過,執行緒、協程和多處理這三種技術都面臨類似的問題,即在 Python 中實現它們並不難,但是程式碼看起來很笨拙,很難讀懂,尤其是對於初學者。

像 Clojure 、 Go 和 Haskell 這樣的語言,在並行性和併發性方面要好得多。如果你處理的不是緩慢或密集型任務,就無需考慮。但如果你是,你可能要重新考慮你的選擇。

如果你要開始一個大專案,不要使用 Python......

用什麼代替Python

Python 自有其不可忽視的優勢,但它也的確存在缺點。

如果你想要明確宣告的變數和開發良好的包,避免陷入依賴地獄,那麼 C 就很不錯。

如果你想要的東西是可移植到任何機器的,那麼 Java, Clojure 或 Scala 是很好的選擇。它們是在虛擬機器上執行的,所以你不會遇到像 Python 一樣的麻煩。

而如果你想執行大型緩慢的任務,你可能會想試試 Go 或者 Haskell。一開始,它們比 Python 難學,但是你投入的時間是有回報的。

你還可以組合不同的語言,比如 Python 非常適合快速編寫指令碼、草圖,甚至是中等規模的專案。我認識的許多開發人員都是用 Python 編寫第一稿和測試執行,然後用 C 、Go 或 Clojure 重寫重要部分。這可以使程式碼執行得更快,同時還可以享受 Python 提供的優勢。

在大型專案中也並不是不能使用 Python,只是很多情況下它並不是唯一:你可以使用 Python 來拼湊 C 、 Go 或 Clojure 中的各個部分。

如果你已經達到了自己構建的目標,請記住,沒有任何一種語言是完美的。儘管 Python 有其缺點,但它依舊很棒,也很方便,你可以透過整合其他語言的程式碼來避開 Python 這些缺點。

原文地址:https://thenextweb。com/news/dont-use-python-for-big-projects

開啟App看更多精彩內容

相關文章

頂部