python高效能程式設計方法一

pythontab發表於2013-05-22

閱讀 Zen of Python,在Python解析器中輸入 import this. 一個犀利的Python新手可能會注意到"解析"一詞, 認為Python不過是另一門指令碼語言. "它肯定很慢!"

毫無疑問Python程式沒有編譯型語言高效快速. 甚至Python擁護者們會告訴你Python不適合這些領域. 然而,YouTube已用Python服務於每小時4千萬影片的請求. 你所要做的就是編寫高效的程式碼和需要時使用外部實現(C/C++)程式碼. 這裡有一些建議,可以幫助你成為一個更好的Python開發者:

1. 使用內建函式:    你可以用Python寫出高效的程式碼,但很難擊敗內建函式. 經查證. 他們非常快速.2.使用join()連線字串.      你可以使用 "+" 來連線字串. 但由於string在Python中是不可變的,每一個"+"操作都會建立一個新的字串並複製舊內容. 常見用法是使用Python的陣列模組單個的修改字元;當完成的時候,使用 join() 函式建立最終字串.

     >>> #This is good to glue a large number of strings

     >>> for chunk in input():

     >>>    my_string.join(chunk)

3. 使用Python多重賦值,交換變數

   這在Python中即優雅又快速:

     >>> x, y = y, x

     這樣很慢:

     >>> temp = x

     >>> x = y

     >>> y = temp          

4. 儘量使用區域性變數

   Python 檢索區域性變數比檢索全域性變數快. 這意味著,避免 "global" 關鍵字.

5. 儘量使用 "in"

     使用 "in" 關鍵字. 簡潔而快速.

     >>> for key in sequence:

     >>>     print “found”

6. 使用延遲載入加速

     將 "import" 宣告移入函式中,僅在需要的時候匯入. 換句話說,如果某些模組不需馬上使用,稍後匯入他們. 例如,你不必在一開使就匯入大量模組而加速程式啟動. 該技術不能提高整體效能. 但它可以幫助你更均衡的分配模組的載入時間.

7. 為無限迴圈使用 "while 1"

   有時候在程式中你需一個無限迴圈.(例如一個監聽套接字的例項) 儘管 "while true" 能完成同樣的事, 但 "while 1" 是單步運算. 這招能提高你的Python效能.

      >>> while 1:

     >>>    #do stuff, faster with while 1

     >>> while True:

     >>>    # do stuff, slower with wile True

8. 使用list comprehension

   從Python 2.0 開始,你可以使用 list comprehension 取代大量的 "for" 和 "while" 塊. 使用List comprehension通常更快,Python解析器能在迴圈中發現它是一個可預測的模式而被最佳化.額外好處是,list comprehension更具可讀性(函數語言程式設計),並在大多數情況下,它可以節省一個額外的計數變數。例如,讓我們計算1到10之間的偶數個數:

     >>> # the good way to iterate a range

     >>> evens = [ i for i in range(10) if i%2 == 0]

     >>> [0, 2, 4, 6, 8]

     >>> # the following is not so Pythonic

     >>> i = 0

     >>> evens = []

     >>> while i < 10:

     >>>    if i %2 == 0: evens.append(i)

     >>>    i += 1

     >>> [0, 2, 4, 6, 8]

9. 使用xrange()處理長序列:

   這樣可為你節省大量的系統記憶體,因為xrange()在序列中每次呼叫只產生一個整數元素。而相反 range(),它將直接給你一個完整的元素列表,用於迴圈時會有不必要的開銷。

10. 使用 Python generator:

     這也可以節省記憶體和提高效能。例如一個影片流,你可以一個一個位元組塊的傳送,而不是整個流。例如,      

     >>> chunk = ( 1000 * i for i in xrange(1000))

     >>> chunk

     <generator object at 0x7f65d90dcaa0>

     >>> chunk.next()

     0

     >>> chunk.next()

     1000

     >>> chunk.next()

     2000

11. 瞭解itertools模組:

     該模組對迭代和組合是非常有效的。讓我們生成一個列表[1,2,3]的所有排列組合,僅需三行Python程式碼:

     >>> import itertools

     >>> iter = itertools.permutations([1,2,3])

     >>> list(iter)

     [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

12. 學習bisect模組保持列表排序:

     這是一個免費的二分查詢實現和快速插入有序序列的工具。也就是說,你可以使用:

     >>> import bisect

     >>> bisect.insort(list, element)

     你已將一個元素插入列表中, 而你不需要再次呼叫 sort() 來保持容器的排序, 因為這在長序列中這會非常昂貴.

13. 理解Python列表,實際上是一個陣列:

     Python中的列表實現並不是以人們通常談論的電腦科學中的普通單連結串列實現的。Python中的列表是一個陣列。也就是說,你可以以常量時間O(1) 檢索列表的某個元素,而不需要從頭開始搜尋。這有什麼意義呢? Python開發人員使用列表物件insert()時, 需三思. 例如:>>> list.insert(0,item)

     在列表的前面插入一個元素效率不高, 因為列表中的所有後續下標不得不改變. 然而,您可以使用list.append()在列表的尾端有效新增元素. 挑先deque,如果你想快速的在兩插入或時。它是快速的,因為在Python中的deque用雙連結串列實現。不再多說。  

14. 使用dict 和 set 測試成員:      檢查一個元素是在dicitonary或set是否存在 這在Python中非常快的。這是因為dict和set使用雜湊表來實現。查詢效率可以達到O(1)。因此,如果您需要經常檢查成員,使用 set 或 dict做為你的容器.

     >>> mylist = ['a', 'b', 'c'] #Slower, check membership with list:

     >>> ‘c’ in mylist

     >>> True

     >>> myset = set(['a', 'b', 'c']) # Faster, check membership with set:

     >>> ‘c’ in myset:

     >>> True

15. 使用Schwartzian Transform 的 sort():

     原生的list.sort()函式是非常快的。 Python會按自然順序排序列表。有時,你需要非自然順序的排序。例如,你要根據伺服器位置排序的IP地址。 Python支援自定義的比較,你可以使用list.sort(CMP()),這會比list.sort()慢,因為增加了函式呼叫的開銷。如果效能有問 題,你可以申請Guttman-Rosler Transform,基於Schwartzian Transform. 它只對實際的要用的演算法有興趣,它的簡要工作原理是,你可以變換列表,並呼叫Python內建list.sort() - > 更快,而無需使用list.sort(CMP() )->慢。

16. Python裝飾器快取結果:

     “@”符號是Python的裝飾語法。它不只用於追查,鎖或日誌。你可以裝飾一個Python函式,記住呼叫結果供後續使用。這種技術被稱為memoization的。下面是一個例子:

     >>> from functools import wraps

     >>> def memo(f):

     >>>    cache = { }

     >>>    @wraps(f)

     >>>    def  wrap(*arg):

     >>>        if arg not in cache: cache['arg'] = f(*arg)

     >>>        return cache['arg']

     >>>    return wrap

     我們也可以對 Fibonacci 函式使用裝飾器:

     >>> @memo

     >>> def fib(i):

     >>>    if i < 2: return 1

     >>>    return fib(i-1) + fib(i-2)

   

    這裡的關鍵思想是:增強函式(裝飾)函式,記住每個已經計算的Fibonacci值;如果它們在快取中,就不需要再計算了.

17. 理解Python的GIL(全域性直譯器鎖):

     GIL是必要的,因為CPython的記憶體管理是非執行緒安全的。你不能簡單地建立多個執行緒,並希望Python能在多核心的機器上執行得更快。這是因為 GIL將會防止多個原生執行緒同時執行Python位元組碼。換句話說,GIL將序列化您的所有執行緒。然而,您可以使用執行緒管理多個派生程式加速程式,這些程 序獨立的執行於你的Python程式碼外。

18. 像熟悉文件一樣的熟悉Python原始碼:

     Python有些模組為了效能使用C實現。當效能至關重要而官方文件不足時,可以自由探索原始碼。你可以找到底層的資料結構和演算法。 Python的原始碼庫就是一個很棒的地方:http://svn.python.org/view/python/trunk/Modules

結論:

這些不能替代大腦思考. 開啟引擎蓋充分了解是開發者的職責,使得他們不會快速拼湊出一個垃圾設計. 本文的Python建議可以幫助你獲得好的效能. 如果速度還不夠快, Python將需要藉助外力:分析和執行外部程式碼.我們將在本文的第二部分中涉及.  


相關文章