這是幾個月前在 EuroPython 郵件列表(主要用來組織和計劃 EuroPython 會議的郵件列表)出現的問題。這是一個很是有意思的問題,我看到這個詞被無數次地使用,但鮮有人嘗試解釋它的含義。在這條線索以後,許多不一樣的人,包括我本身,都給出了本身的答案。如今我把個人答案放到博客上,而且潤色了一下,但願它能對您有所增益。 java
Pythonnic 是一個模糊的概念,儘管沒有「智能」或「生命」那麼模糊,但當你嘗試定義它們的時候,就像去抓住一條滑溜溜的泥鰍同樣無從下手。但是雖然它們難以定義,然而並不意味着它們沒用,由於事實上人們其實極善於利用混亂的定義。Pythonic 有點像「Python慣用法」的意味,如今讓咱們來聊聊它真正的含義。 python
隨着時間的推移,Python語言不斷演進,社區不斷成長,涌現了許多關於如何正確地使用 Python 的 ideas。一方面 Python 語言推薦使用大量的慣用法來完成任務(「完成任務的惟一方法」),另外一方面,社區不斷演變的新的慣用法的又反過來影響了語言的進化,以更好地支持慣用法。好比新進入的字典的 .get() 方法,它把 has_key() 和元素存取兩個操做組合爲一個操做,從中能夠看出這種進化。 程序員
慣用法每每不能直接從其它編程語言移植過來。以下文是實現對一個序列裏的每一個元素執行一個操做的 C 語言實現: 編程
for (i=0; i < mylist_length; i++) { do_something(mylist[i]); }
直接的等效 Python 代碼是這樣的: 數據結構
i = 0 while i < mylist_length: do_something(mylist[i]) i += 1
這段代碼可以完成工做,但並不 Pythonic,它並非 Python 語言推薦的慣用法。讓咱們來改進一下。典型的 Python 慣用法是用內置的 range() 函數生成全部的序列下標: 框架
for i in range(mylist_length): do_something(mylist[i])
其實這種實現也並不 Pythonic,接下來你們看看語言推薦的實現方式,真正 Pythonic 實現: less
for element in mylist: do_something(element)
void foo(int* a, float* b) { *a = 3; *b = 5.5; } ... int alpha; int beta; foo(&alpha, &beta);
在 Python 中能夠用很囧很噁心的方法來實現:經過給函數傳遞序列參數來返回結果。寫出來的代碼可能像這樣: 編程語言
def foo(a, b): a[0] = 3 b[0] = 5.5 alpha = [0] beta = [0] foo(alpha, beta) alpha = alpha[0] beta = beta[0]
顯然這是毫無 Pythonic 可言的實現。Python 中讓函數返回多個值的慣用法與此迥異,得益於元組和元組解包,它看起來也要漂亮得多: ide
def foo(): return 3, 5.5 alpha, beta = foo()
在經驗老到的 Python 程序員看來,不夠 Pythonic 的代碼每每看起來古怪並且累贅,過於冗餘也難以理解。由於它使用冗長的代碼代替常見的、公認的、簡短的慣用法來實現預期效果。更甚於此的是在語言支持正確的慣用法以後,非推薦的代碼一般執行起來更慢。 函數
Pythonic 就是以清晰、可讀的慣用法應用Python 理念和數據結構。舉個例子,應該多使用動態類型,在無必要之處引入靜態類型就走向了另外一端。另外也要避免使用經驗豐富的 Python 程序員不熟悉的方式去完成任務(即遵循最小驚奇原則)。
Pythonic 一詞也可以適用於底層的慣用法。一個 Pythonic 的庫或框架能使程序員更加容易、更加天然地學會利用它來完成任務。若是用 Python 編寫的庫或框架迫使程序員編寫累贅的或不推薦的代碼,那麼能夠說它並不Pythonic。也許多是爲了使這個庫更加方便、易懂,而沒有應用 Python 的一些理念,如類等,那也是不 Pythonic 的。類定義應當儘量地實現信息隱藏,雖然 Python 的許多操做都只做「寬鬆限制」(一般由程序員在屬性的前面加上一個下劃線來暗示這是私有成員),但也要作得像 Java 那樣嚴格。
固然,當規模很大的時候,它是否 Pythonic 就極具爭議性了。這裏給出一些參考條款:如減小冗餘,Python 的庫與 APIs 都傾向於小型化和輕量化(相對於 java 程序庫而言)。重量級的、API過於細化的的Python 庫並不 Pythonic。好比 W3C XML DOM API,儘管它的 Python 實現已經很有時日,但你們並不認爲它 Pythonic。有些人認爲它是 Java 式的,雖然也有許多 Java 程序員認爲並不如此。
一個Pythonic的框架不會對已經用慣用法完成的東西重複發明輪子,並且它也遵循經常使用的 Python 慣例。
固然,問題是構建框架時確定會不可能避免地引入一些你不熟悉的模式和方法。Zope2 是我極爲熟悉的一個框架,它也是一個引入了許多完成工做的特定的方法(如 Acquisition)的例子,這些方法每每什麼地方都用不到,所以許多經驗豐富的 Python 程序員認爲它並不 Pythonic。
建立 Pythonic 的框架極其困難,什麼理念更酷、更符合語言習慣對此毫無幫助,事實上這些年來優秀的 Python 代碼的特性也在不斷演化。好比如今認爲像 generators、sets、unicode strings 和 datetime 之類的特性尤其 Pythonic。Zope2 的歷史悠久,它從1997年開始開發,你不能把不夠 Pythonic 歸咎於它,甚至考慮到這麼多年來它控制得如此之好,更應該感謝它。
關於 Pythonicness 的新趨勢的一個例子是Python 的包和模塊結構日益規範化。新的代碼庫如 Twisted、Zope3 和 PyPy 等或多或少都跟隨了這樣的潮流:
在我寫庫(如 lxml)的時候也遵循了這樣的慣例。
由於更多人認爲一個 Python 程序員容易學習的功能不那麼強大的框架比一個須要大量時間來學習的強大系統更爲 pythonic。因此有時我認爲宣稱軟件不夠 Pythonic 不公平,甚至可能會所以而掩蓋了該軟件積極的一面。
最後,做爲何是 Pythonic 的擴充材料,能夠嘗試一下在 Python 解釋器裏執行以下語句:
import this
>>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!