作者:Phillip J. Eby.
翻譯:ShiningRay @ NirvanaStudio
原文地址:http://dirtsimple.org/2004/12/python-is-not-java.html
我最近正在看一個(gè)基于wxPython的GUI應用程序,大概45.5KLOC的樣子,但我沒(méi)有計算它用到的庫的大?。ㄈ鏣wisted)。代碼是由那些對Python相對生疏的Java的開(kāi)發(fā)者寫(xiě)的,所以程序有很?chē)乐氐男阅軉?wèn)題(如三十秒的啟動(dòng)時(shí)間)。我在檢查代碼的時(shí)候發(fā)現他們寫(xiě)了很多對Java有意義但是對Python卻很恐怖的東西。并不是因為“Python比Java慢”,而是因為在Python中有更方便的方法去完成同樣的目標,甚至在Java中不可能的事情。
所以,可悲的事就是這些可憐人事倍功半,產(chǎn)生了很多很多不需要寫(xiě)的代碼,從而比相應合乎Python習慣的寫(xiě)法慢得多得多。我們來(lái)看一些例子:
-
在Java中一個(gè)靜態(tài)的方法(static)不能翻譯成一個(gè)Python的類(lèi)方法(classmethod)。哦,當然,多多少少他最終產(chǎn)生類(lèi)似的 效果,但類(lèi)方法的目的實(shí)際上是做了一些通常在Java中不可能的事(如繼承一個(gè)非默認的構造函數)。Java靜態(tài)方法的習慣翻譯通常是一個(gè)模塊級函數,而 不是一個(gè)類(lèi)方法或靜態(tài)方法(staticmethod)。(同時(shí)靜態(tài)封閉(final)字段應該翻譯成模塊級常量。)
這并不是一個(gè)性能上的問(wèn)題,但是一個(gè)Python程序員要用像這些類(lèi)似Java習慣的代碼的話(huà),可能就會(huì )被在該輸入 Foo.someFunction時(shí)卻要輸入Foo.Foo.someMethod這種情況給惹毛了。但是請注意:調用一個(gè)類(lèi)方法將會(huì )比調用一個(gè)靜態(tài)方法 和函數要多一部分額外的內存。
啊,那些Foo.Bar.Baz也不是省油的。在Java中,這些點(diǎn)分割的名稱(chēng)是由編譯器去查找的,所以運行時(shí)根本無(wú)所謂你有多少點(diǎn)。在 Python中,每次運行時(shí)都要查找,所以每個(gè)點(diǎn)都要計算在內。(Python中一定要記住這點(diǎn),“平鋪比嵌套好”,盡管比起性能,他和“可讀性”和“簡(jiǎn) 單就是美”更靠近。)
- 要用switch語(yǔ)句?Python翻譯將是一個(gè)哈希表,不是一堆if-then語(yǔ)句。用一堆if-then在Java中也不是switch語(yǔ) 句,如果有字符串參與了呢?他其實(shí)是一個(gè)哈希表。CPython字典實(shí)現用了性能最佳—在我們宇宙中目前所知道的—的哈希表的實(shí)現之一。你自己所寫(xiě)的代碼 也不會(huì )比這個(gè)再好了,除非你是Guido、Tim Peters和Raymond Hettinger的“私生子”——還是遺傳增強了的。
-
XML不是答案。它也不是一個(gè)問(wèn)題。要在正則表達式上解釋Jamie Zawinski,“一些人,當遇到一個(gè)問(wèn)題的時(shí)候,就想‘我知道,我要用XML’那這個(gè)時(shí)候,他們就有兩個(gè)問(wèn)題了。”
和Java比這個(gè)一個(gè)不同的情況,因為比起Java代碼,XML是輕巧而且有彈性的。但比起Python的代碼來(lái),XML就是一個(gè)船錨,一個(gè)絆腳 石。在Python中,XML是用來(lái)做交換,而不是你的核心功能,因為你不需要這么做。在Java中,XML可能是你的大救星因為他讓你實(shí)現了特定領(lǐng)域的 語(yǔ)言并“不通過(guò)編碼”提高了你的應用程序的適應性。在Java中,避免編碼是一個(gè)很大的優(yōu)勢,因為編碼意味著(zhù)重新編譯。但在Python中,更常見(jiàn)的是, 寫(xiě)代碼比寫(xiě)XML更方便簡(jiǎn)單。同時(shí)Python處理代碼要遠遠比處理XML快。(不僅僅是這個(gè),你必須書(shū)XML處理代碼,同時(shí)Python自身就已經(jīng)為你 準備好了。)
如果你是一個(gè)Java程序員,對于你是否要在你的Python核心應用中使用XML作為一部分,不要相信你的本能。如果你不是因為信息交互的原因去 實(shí)現一個(gè)已經(jīng)存在的XML標準或是建立某種導入、導出格式或者建立某種XML編輯器或處理工具,那么就不要這么做。一次也別。甚至連想都不要想?,F在,扔 掉那個(gè)XML模式把你的手解放吧!如果你的應用程序或者平臺要被Python開(kāi)發(fā)者使用,他們只會(huì )感謝你不要在他們的工作量中添加使用XML的負擔。
(這里唯一的例外是如果你的受眾的的確確,確確實(shí)實(shí)需要XML,出于某種奇怪的理由。像,他們拒絕學(xué)習Python并只對你使用了XML而付錢(qián)給 你,或者你打算給他們一個(gè)編輯XML的GUI,同時(shí)這個(gè)寫(xiě)XML的GUI呢是另一個(gè)人寫(xiě)的,同時(shí)你得到免費使用的權利。還有一些很少見(jiàn)的架構上的原因需要 用到XML。相信我,他們不會(huì )出現在你的程序中。如果有疑問(wèn),對一個(gè)資深的Python開(kāi)發(fā)員解釋你的用例?;蛘?,如果你臉皮厚的話(huà),試試向一個(gè)Lisp 程序解釋你的程序為什么要用XML!)
-
Getter和setter是壞蛋。壞蛋,魔鬼!Python對象不是Java Bean。不要寫(xiě)什么getter和setter,然后還把它們包裝在“屬性”里面。它直到你能證明你需要比一個(gè)簡(jiǎn)單訪(fǎng)問(wèn)復雜一點(diǎn)的功能時(shí)才有意義,否 則,不要寫(xiě)getter和setter。它們是CPU時(shí)間的浪費,更要緊的是,它們還是程序員寶貴時(shí)間的極大浪費。不僅僅對于寫(xiě)代碼和測試的人,對于那些 要閱讀和理解它們的人也是。
在Java中,你必須使用getter和setter因為公共字段不允許你以后改變想法再去使用getter和setter。在Python中,這 樣做很傻,因為你可以以一個(gè)普通特性開(kāi)始并可以在任何時(shí)間改變你的想法,而不用影響到這個(gè)類(lèi)的任何客戶(hù)。所以不要寫(xiě)getter和setter。
-
代碼重復在Java中常常是一個(gè)不得不要的魔鬼,你必須經(jīng)常一遍一遍寫(xiě)同一個(gè)方法而只有一點(diǎn)點(diǎn)的變化(通常是因為靜態(tài)類(lèi)型約束)。在Python中 這樣做是沒(méi)有必要的也是不值得的(除了極少數一些特定的場(chǎng)合需要內聯(lián)一些要求性能的函數)。如果你發(fā)現自己一遍一遍在寫(xiě)同樣的代碼而且變化很少,你就需要 去學(xué)一下閉包。他們并不是真的很可怕。
這就是你要做的。你寫(xiě)了一個(gè)包含了函數的函數。這里內部的函數就是你要一遍遍寫(xiě)的函數的模版,但是在里面加入了針對不同情況的函數要使用變量。外部 的函數需要剛剛提高的那種變量作為參數,并且將內部的函數作為結果返回。然后,每次你要寫(xiě)另一種略微不同的函數的時(shí)候,你只要調用這個(gè)外部的函數,并且把 返回值賦給你要讓“重復”函數出現的名字?,F在,如果你需要改變這個(gè)工作方式,你只要改變一個(gè)地方:這個(gè)模版。
在我所看過(guò)的應用程序/平臺中,只有一個(gè)很微不足道的程序使用了這個(gè)技術(shù)之后可以去掉數百行重復代碼。事實(shí)上,自從開(kāi)發(fā)者使用了特別的樣板文件來(lái)為這平臺開(kāi)發(fā)插件,這會(huì )節省很多很多第三方開(kāi)發(fā)人員的代碼,同時(shí)也使那些程序員要學(xué)習的東西簡(jiǎn)化了。
這只是Java->Python思維方式轉變的冰山一角而已,現在我可以讓他轉變成正確的而不用鉆研這個(gè)程序的細節。本質(zhì)上,如果你曾經(jīng)用過(guò)一段時(shí)間Java,而且對Python比較陌生,不要太相信自己的本能。你的本能已經(jīng)為Java調節,而不是Python。向后退一步,最重要的,不要寫(xiě)這么多代碼了。
要這樣做,讓自己覺(jué)得更加需要Python。假裝好像Python是可以做任何你想做的魔棒,卻讓你無(wú)須動(dòng)一個(gè)手指。問(wèn)一下,“Python是怎樣解決我的問(wèn)題的?”還有“Python語(yǔ)言的哪個(gè)特點(diǎn)和我的問(wèn)題最相似?”你絕對會(huì )驚訝于你需要的東西其實(shí)已經(jīng)有了某種固定形式。事實(shí)上,這種現象實(shí)在是太普遍了,甚至在很有經(jīng)驗的Python程序員中也會(huì )出現,以至于Python社區中給這種現象起了個(gè)名字。我們稱(chēng)之為“GUIDO的時(shí)間機器”(GUIDO是美語(yǔ)中太空飛行工程師的意思),因為有時(shí)候看上去得到我們所需要的東西好像只有他知道的一種方法,但當我們自己知道了就不一樣了。
所以,如果你不能感到你在使用Python時(shí)至少比用Java要多出10倍的生產(chǎn)力,?。ㄍ瑫r(shí)如果你還懷念你的Java IDE,考慮一下這種可能性:因為你寫(xiě)的Python程序比他所需要的要復雜得多)
附錄:(翻譯自此篇文章的評論)
確實(shí),哈希表==字典。舉個(gè)最簡(jiǎn)單的例子,從Python
標準庫中檢出“pickle”和“copy”模塊,這兩個(gè)模塊會(huì )從字典中查找類(lèi)型并調用相應的函數。另一個(gè)有些詭異的例子是范型函數,我已經(jīng)在最近的Blog中寫(xiě)了一下。
關(guān)于閉包的例子,我這里給出一個(gè)很笨的例子。假設你要寫(xiě)很多這樣的函數:
def addOne(x): return x+1
def addTwo(x): return x+2
然后你可以這樣寫(xiě):
def makeAdder(addend):
… def add_it(x): return x+addend
… return add_it
并且這樣使用:
addOne = makeAdder(1)
addTwo = makeAdder(2)
這樣就可以等同于原來(lái)的定義了。

