最近兩個月都在寫 Python,反而 Ruby 快忘光了...
由於先前有 Ruby 的底子,轉學 Python 的進入門檻低了許多,一年前我和 Scott 聊過,我提些 Ruby 功能,Scott 就提 Python 要怎麼寫;或是反過來進行。當時就覺得這兩個語言的功能很像。經過兩個月的練習,現在我反而比較熟 Python,真是諷刺啊。
個人覺得 Python 和 Ruby 的主要不同是哲學層次的,Ruby 講求人性,給予使用者 (programmer) 極大的彈性;Python 則是有個明確的規則在,講求一致性。「Form is liberating」,我並不討厭 Python 的做法。舉例來說,Ruby 會提供許多別名方法 (alias method),讓使用者可以憑直覺用對功能,比方加一個元素到陣列裡,在 Ruby 裡有以下的寫法:
a = [] a.push 3 a.push(3) a << 3
但在 Python 裡,就只有 a.append(3) 一種。並且,Ruby 讓使用者可以在任何時刻改變任意物件的內容。所以 Ruby 的使用者不用擔心拿不到 third party library 的 source code,反正有啥難處理的地方,在 runtime 直接修改即可。方便歸方便,在大型專案裡意味著寫完函式庫後,作者無法保證使用該函式庫的人會讓函式庫元件照規矩運作,這使得程式碼協同運作充滿危機。
相較於 Ruby 強調直覺帶來的便利,Python 則是制定好最佳規則,照規則辦事。以 exception 來說,Python 遵從有意外就用 exception 的規則,除以零是 exception,dictionary (即 hash table)取用不存在的 key 也是 exception,取用不存在的變數也是 exception。反觀 Ruby,以人性的角度出發,這三者狀況的反應分別為:exception、nil (Ruby 的 null)、exception。從這可看出 Ruby 對 hash table 取用不存在的 key 的行為不一致,但這麼做確實比較方便,省去先查 key 是否存在再取值的動作。另外以查 method 為例,Ruby 的查法很直接(直覺):
[].methods.grep /push/
[].methods 就會顯示 [] 所有方法; methods.grep 就會對 methods 做字串比對。反觀 Python 則是透過 __dict__ 的機制達成:
[s for s in dir([]) if "p" in s] # dir() 會傳回 [].__dict__ 和 [] 父類別的 __dict__
Python 另對 __dict__ 有許多有趣的設計,像是 hasattr(),還有讓 __dict__ 替 function 帶來許多好處 (如 cache,可做到類似 C 裡 function 的 static variable 的效果),這部份太細了,不在此談論。
另一方面,兩者都強調 Everything is object,但對 object 的定義不同,Python 對 object 的定義可參見:《Python Types and Objects》:The Object Within。Ruby 的 OO 很強,Python 沒有 private/protected/package-private/public 這類流行的 OO scope 字眼,反而用變數命名前加底線,暗示使用者有底線開頭的物件是 private/protected,但使用者仍能直接存取。對我來說,我認為 private/protected/package-private/public 實在太難設了,若非經驗豐富的程式設計師,很難訂對,所以不訂也還過得去,省下煩惱 scope 的時間。
除 Unicode 支援良好外,Python 另一令我激賞的特性是 self-document。Python 規定 module、class、function 的文件必須寫在 module 的開頭、class 的下一行、function 的下一行。造成的結果是,許多工具如 ipython 能輕易地將程式碼內的文件顯示出來。只有有些寫程式和文件經驗的人就能明白,將文件和程式分開寫是多麻煩的事,通常下場是文件漏寫,或是文件過期。所以文件和程式愈能合為一體,愈有助於程式碼品質 (code quality)。
其它如 thread 的差異,和 C/C++ 結合的容易度,我還沒碰到無法比較,到是 interpreter 部份值得一提。個人認為好的入門書、好的文件和 interpreter 可大幅減低入門門檻,是否有好書取決於作者功力,這部份兩邊都有不錯的書;文件部份如前所述,Python 的設計相當棒,;而 interpreter 部份,ipython 實在太強了,像 Scott 是直接在 ipython 上寫,再存成 script 檔。經過這兩個月的洗禮,我決定先投向 Python 陣營,只好忍痛割捨 Ruby 寫碼的爽快感。