標籤:

[技術] 談談Python

昨天的文章收穫了不少有價值的回復。不少人發現了一個大bug,那就是「上帝的歸上帝,撒旦的歸撒旦」。囧死我了。腦手不同步這病怎麼治啊~以後我寫完文章爭取好好複查一遍。

有個名叫「舟」的讀者寫了段很棒的評論,不敢獨專,和大家分享:

幾點看法,隨便談談:1.應該是「上帝的歸上帝,凱撒的歸凱撒」,原意講的是宗教和世俗的關係,很深刻,變成「撒旦的歸撒旦」以後這句話的意思其實就很讓人費解了… 2. IoC的確是個很好的東西,但我認為它和libc中那種供應用層調用的函數之間並不是一種簡單的進化關係,二者是互補的缺一不可,從兩個不同方向減少軟體的重複。libc中其實也有IoC的東西,例如qsort。libc畢竟是一個底層庫提供的是應用和系統的介面,所以不需要太多IoC的東西。不過IoC確實比那種簡單供應用調用的函數更深奧一點,新手更不容易掌握一點,所以更值得拿出來說。3. 不覺得AOP與OOP/FP是一個量級的東西,AOP在OOP中完全可以通過Decorator模式解決,在FP中就更直接了當了,python中函數的decorator就是一種典型的高階函數,是FP的東西。 今天討論的這些東西其實都是一言難盡的,隨便一段單獨拿出來都夠程序君寫一整篇文章的了,哈哈

這個周末要給人培訓python,所以乾脆今天先講講python,作為預演。我之前的文章也在不同的場合都建議初學者學習python。原因在於python良好的文化,和豐富的應用場景。當然,很多我接觸過的語言都很好,學起來也很有滋味,但是。。。誰讓有句俗語說:「真正的程序員用C,聰明的程序員用python」呢。

(今天的文字最好還是橫過來看)

文化和關懷

請打開任何一個python解釋器,在裡面輸入:import this。我所了解的任何一門其他語言都沒有如此巧妙而又大張旗鼓地將其文化寫入了語言本身。

The Zen of Python, by Tim PetersnnBeautiful is better than ugly.nExplicit is better than implicit.nSimple is better than complex.nComplex is better than complicated.nFlat is better than nested.nSparse is better than dense.nReadability counts.nSpecial cases arent special enough to break the rules.nAlthough practicality beats purity.nErrors should never pass silently.nUnless explicitly silenced.nIn the face of ambiguity, refuse the temptation to guess.nThere should be one-- and preferably only one --obvious way to do it.nAlthough that way may not be obvious at first unless youre Dutch.nNow is better than never.nAlthough never is often better than *right* now.nIf the implementation is hard to explain, its a bad idea.nIf the implementation is easy to explain, it may be a good idea.nNamespaces are one honking great idea -- lets do more of those!n

這段文字值得好好消化。你一定會好奇,this究竟是什麼?如果你對python的package機制有了解,那麼,你就應該知道該從python的安裝路徑下找這個源碼this.py,打開一看,一段格式熟悉,但亂七八糟的文字,然後是一段代碼:

d = {}nfor c in (65, 97):n for i in range(26):n d[chr(i+c)] = chr((i+13) % 26 + c)nnprint "".join([d.get(c, c) for c in s])n

看到這裡,你會會心一笑,這就是程序員的無聊和可愛之處,也是一種隱藏在geek范(作者是在向凱撒密碼致敬么?)下的人文關懷。

我喜歡python除了喜歡代碼的寫法,另外一個原因是喜歡讀python的代碼。其實我們工作的大部分時間在讀別人的代碼。讀python代碼總是比讀其它代碼舒服些。除了格式統一外(很高興go也在這方面通過gofmt做了努力),python的文化某種程度上保證了其代碼的可讀性。

python的人文關懷還體現在無處不在的dir和help上。在python shell(建議安裝ipython)里,只要有了這兩個武器,再加上一些必要的練習,你就能很快掌握一個新的庫。還有什麼比對初學者友好更友好的事情呢?

應用場景

python可以應用在很多場合:

  • 小工具,小腳本
  • 文本處理
  • 圖形處理
  • 爬蟲
  • 伺服器
  • 網站
  • 數值運算
  • 構造原型
  • ...

基本上這些領域都有很棒的python庫供你驅使。

語言能力

python的語言能力中規中矩,表現力稍弱於ruby。但它還是涵蓋了從面向對象,函數式編程(有限支持)到元編程(有限支持)的主流思想和方法。由於昨天的文章中已經有幾個python的例子來說明這一點,這裡就不再重複。作為一個成功的語言,python並未固步自封,它一直在進行有益的進化。比如說2.5新加的with關鍵字,簡化了try..finally結構,讓代碼更簡潔漂亮(這一直是Python努力的方向):

with open(hello.txt) as f:n f.write(Hello world!n)n

這等價於之前的寫法:

f = open(hello.txt)ntry:n f.write(Hello world!n)nfinally:n f.close()n

更關鍵的是,只需實現幾個magic function,這種語言能力便能為你所用:

class MyOpen:n def __init__(self, filename, mode = r):n self.filename = filenamen self.mode = moden def __enter__(self):n self.f = open(self.filename, self.mode)n return self.fn def __exit__(self, **unused):n self.f.close()nnwith MyOpen(hello.txt) as f:n f.write(hello world!n)n

有意思的庫和工具

現代編程語言的競爭是語言能力的競爭,也是語言的庫和工具的競爭。對python而言,標準庫為你提供了各種基本的能力,社區里繁多的第三方庫更是將這種能力推到了一個新的高度。比如說github上著名的"kennethreitz/requests"庫,它讓http client的撰寫簡直就像寫文章一樣簡單直觀:

In [5]: import requestsnnIn [6]: r = requests.get(https://api.github.com, auth=(user, pass))nnIn [7]: r.status_codenOut[7]: 200nnIn [9]: r.json()nOut[9]:n{...}nnIn [10]: r.textnOut[10]: u{"..."}n

利用這樣的類庫,加上github api,你可以十幾二十行代碼就撰寫一個代碼全文搜索工具。這就是web時代的生產力。

再比如scrapy(一個crawler framework),你只需定義好抓取的規則,抓好的數據怎麼存儲,它就能並發地幫你抓取並格式化數據。最有節操的是,它還提供了一個美妙的shell,讓你在其中互動式地不斷試錯,直到可以正確定義好抓取的規則。

並發(concurrency)支持

在2.3里python有了generator,它是coroutine的基石。generator允許你掛起當前的執行點,使得同步的代碼轉為非同步,順序執行的程序具備潛在並行執行(Parallelism)的能力,比如說我們做個fabonacci數列:

def fabonacci():n a, b = 1, 2n while True:n a, b = b, a+bn yield bn

通過generator,代碼一下子具備了lazy evaluation的能力,只有在你需要的時候,數據才被計算出來。也許你在這裡看不到並發的影子,那麼請你想像一下滿是generator的世界,每個generator都有自己的執行棧,如果你寫個scheduler,將generator調入調出,這不就是coroutine么?當然,python有自己的coroutine庫,gevent,基於效率最高的libev。比如用gevent實現actor model(erlang的基石):

import geventnfrom gevent.queue import Queuennclass Actor(gevent.Greenlet):nn def __init__(self):n self.inbox = Queue()n Greenlet.__init__(self)nn def receive(self, message):n raise NotImplemented()nn def _run(self):n self.running = Truenn while self.running:n message = self.inbox.get()n self.receive(message)n

簡單,明了。

Python的缺陷

好吧,任何語言總有其陰暗面。Python(CPython)有個臭名昭著的GIL(當然這也不是python獨有的,ruby也有MRI),全局解釋鎖,任何代碼的執行都必須先獲得這個全局鎖,當有IO操作時釋放這把鎖。有了這個全局鎖,Python的threading實際上是一個虛假的概念,無論你有多少個thread,只能使用一個核。你可以做個threading的實驗:

import threadingnndef dead_loop():n while True:n passnn# new dead loop threadnt = threading.Thread(target=dead_loop)nt.start()nn# dead loop on main threadndead_loop()nnt.join()n

以及multiprocessing的測試:

import multiprocessingnndef dead_loop():n while True:n passnn# new dead loop processnp = multiprocessing.Process(target=dead_loop)np.start()nn# dead loop on main processndead_loop()nnp.join()n

看看二者CPU佔用率的差異。threading很不給力啊!

當然,你無須為此感到太悲觀。多線程用不到Multicore的能力,但多進程可以,雖然多進程開銷大些,但終究能多少彌補GIL帶來的缺憾。

如果你對本文感興趣,歡迎訂閱公眾號『程序人生』(搜索微信號 programmer_life)。每天一篇原汁原味的文章,早8點與您相會。


推薦閱讀:

等待還是放下?
[產品技術] Operational Transformation

TAG:迷思 |