這裡的內容僅僅是本人閱讀《Python高性能編程》後總結的一些知識,用於自己更好的瞭解Python機制。本人現在並不從事計算密集型工作:人工智慧、數據分析等。僅僅只是出於好奇而去閱讀這本書。很多人因為Python不能同時使用多顆CPU(全局解釋器鎖GIL),而覺得它不能實現高性能。書中有很多介紹避開 ...
這裡的內容僅僅是本人閱讀《Python高性能編程》後總結的一些知識,用於自己更好的瞭解Python機制。本人現在並不從事計算密集型工作:人工智慧、數據分析等。僅僅只是出於好奇而去閱讀這本書。很多人因為Python不能同時使用多顆CPU(全局解釋器鎖GIL),而覺得它不能實現高性能。書中有很多介紹避開GIL或者Python虛擬機的方式,例如Cython,PyPy等。
首先我們要說一下時間複雜度,可以幫助我們理解CPython編譯器怎麼幹活:
時間複雜度
在描述演算法複雜度時,經常用到o(1), o(n), o(logn), o(nlogn)來表示對應演算法的時間複雜度, 這裡進行歸納一下它們代表的含義: 這是演算法的時空複雜度的表示。不僅僅用於表示時間複雜度,也用於表示空間複雜度。O後面的括弧中有一個函數,指明某個演算法的耗時/耗空間與數據增長量之間的關係。其中的n代表輸入數據的量。比如時間複雜度為O(n),就代表數據量增大幾倍,耗時也增大幾倍。比如常見的遍歷演算法。再比如時間複雜度O(n^2),就代表數據量增大n倍時,耗時增大n的平方倍,這是比線性更高的時間複雜度。比如冒泡排序,就是典型的O(n^2)的演算法,對n個數排序,需要掃描n×n次。再比如O(logn),當數據增大n倍時,耗時增大logn倍(這裡的log是以2為底的,比如,當數據增大256倍時,耗時只增大8倍,是比線性還要低的時間複雜度)。二分查找就是O(logn)的演算法,每找一次排除一半的可能,256個數據中查找只要找8次就可以找到目標。O(nlogn)同理,就是n乘以logn,當數據增大256倍時,耗時增大256*8=2048倍。這個複雜度高於線性低於平方。歸併排序就是O(nlogn)的時間複雜度。 O(1)就是最低的時空複雜度了,也就是耗時/耗空間與輸入數據大小無關,無論輸入數據增大多少倍,耗時/耗空間都不變。 哈希演算法就是典型的O(1)時間複雜度,無論數據規模多大,都可以在一次計算後找到目標(不考慮衝突的話) 列表和元組 1、列表是動態數組,它們可變且可以重設長度(改變其內部元素個數)。 2、元組是靜態的數組,它們不可變,且其內部數據一旦創建便無法改變。 3、元組緩存與Python 運行時環境,這以為著我們每次使用元組都無需訪問內核去分配記憶體。 當創建的數據量及,達到百萬千萬級以上,合併多個元組,會比一個列表占用更少的空間。 列表在進行append()操作時,會Copy原列表,創建一個更大的列表,然後銷毀原列表。在append時,編譯器會預創建一部分數據空間,用於以後的添加。 元組在進行合併操作(+)時,會創建一個新的元組,然後銷毀舊的元組,元組數據集前後不會發生改變 字典和集合 字典和集合適合於存儲能夠被索引的數據。當你在使用字典和集合處理可以索引的數據時它的時間複雜度是O(1),但是對於那些不能被索引的數據是徒勞無功的。 字典與集合在CPython創建時,會像系統申請定量記憶體塊預設最小長度是8,每次改變大小增加到原來的4倍。每次插入數據時會生成索引(二進位數),會在申請的記憶體存儲塊中隨機插入,如果目標存儲塊已有數據,那就換個位置,這叫做散列碰撞。 由於字典與集合在插入數據不是每一次都會擴增集合體積,所以會比列表效率高效、省記憶體空間。雖然會有散列碰撞,但是每次散列碰撞都是二進位數的比較。 集合:在對一批數據進行去重時,不如把這批數據放入集合中。因為在你使用列表存儲這批數據時,你需要手動判斷是否重覆,而且列表會預創建空桶用於存接下來的數據。而集合是一個純Key的數組,裡面的數據時唯一的。 字典:是key:value的形式存儲 散列函數:在散列函數中會對字典和列表生成二進位數作為掩碼(可以理解為索引,因為在插入、查詢時是依靠這個值)。 應該有一種——而且最好只有唯一的一種——明顯的方式去完成它。雖然這種方式可能一開始並不明顯,除非你是荷蘭人。 ——Tim Peters Python之禪 使用Python的目的是快速實現功能,且代碼能夠穩定運行。至於優化,所花費的時間可能是產品初創到誕生的數倍時間。