在開始之前我們加深一下對“序列”,“對象”,“引用”概念的理解,不太清楚的朋友可以再了解下:
什么是序列,Python中都有哪些序列?
序列中的元素都是有序的,擁有自己的編號(即索引值,也叫下標,默認從0開始),可以通過索引值獲取序列中對應的元素。
Python總共有6個內(nèi)置的序列:字符串、Unicode字符串、列表、元組、buffer對象和 xrange 對象。
序列一般可以做這些操作:索引、長度、切片、遍歷、組合(序列相加)、檢查成員、重復(乘法)、最小值和最大值。
數(shù)據(jù)類型中字符串string,列表list,元組tuple屬于序列類型;
從可變性上講:字符串string,元組tuple屬于不可變序列,列表list屬于可變序列。
不可變序列一般不能對添加,修改和刪除元素,所以也就沒有添加,修改和刪除的操作方法。
什么是對象,什么是引用?
python中一切皆對象。常量,變量,類,函數(shù)等等都可以叫對象。
引用一般是指對象的引用,用來間接操作“對象”的,也可以把引用理解為對象的“別名”,一個對象可以有多個別名,也就是說一個對象可以有多個引用;好比我們?nèi)藖碚f,一個人就是一個具體的對象,他有學名,小名,還有綽號,都指向的是這個具體的“人”對象。
Python作為一種動態(tài)類型的語言,對象和引用是分離的,通過“引用計數(shù)”來跟蹤記錄已經(jīng)分配的內(nèi)存;
當一個對象被創(chuàng)建或者被賦值時,它的初始引用計數(shù)為1,當有其他變量也被賦值到這個對象時,引用計數(shù)就會增加;
當這個對象不再被其它對象引用時,引用計數(shù)就會減小,直到等于0時GC就會回收對象,該對象也就被徹底銷毀了。
我們可以把上面的值“123”理解為“對象”;a,b,c理解成對象“123”的引用。
等號 = 我們通常叫賦值,可以理解成給對象“123”起別名。
a,b,c這三個引用指向同一個內(nèi)存地址,也可以說這三個引用指向同一個對象,引用本身存儲的是一個內(nèi)存地址。
如果我們把 a 這條引用刪除掉,引用計數(shù)會減小,其它引用是不受影響的。
當把a,b,c三條引用全部刪除掉,引用計數(shù)變成0,對象將被GC回收,釋放內(nèi)存。
Python中一切皆對象,所以 a,b,c這三條引用我們通常也叫對象。
今天我們簡單分析下Python中幾個常見的數(shù)據(jù)結構:列表list,元組tuple,字典dict,集合set;
數(shù)據(jù)結構:線性表和鏈表、堆棧和隊列、樹和二叉樹、圖、字典和集合、B樹、哈希表。
列表list和元組tuple都是由鏈表(Linked list)實現(xiàn)的,即列表是鏈表存儲結構,這也解釋了為什么列表和元組都是有序的。
線性表分為順序表和鏈接表兩種,鏈表是線性表中的第二種,所以并不會按線性的順序存儲數(shù)據(jù),而是在每一個節(jié)點里存著下一個節(jié)點的指針。
字典dict和集合set都是由哈希表(hash table)實現(xiàn)的,所以都是無序的。
字典dict和集合set都是用空間來換時間,占用空間大,但檢索效率高(和元素多少無關);
因為少了key的存儲,集合set比字典dict占用的空間相對會小些。
列表list占用空間相對較少,添加和修改元素效率高,但會隨著元素個數(shù)越來越多,檢索查詢速度會越來越慢。
元組與列表很類似,不同之處在于元組的元素不能修改,還有列表在創(chuàng)建,變更等操作時需要向系統(tǒng)內(nèi)核申請空間,元組則是緩存于Python運行時環(huán)境中,意味著每次使用元組時無須訪問內(nèi)核去分配內(nèi)存。
如果想測試一段代碼的運行時間可以使用timeit模塊:
我們可以看出在初始化10個相同元素的列表和元組時,元組要比列表快了6倍多,比用append()初始化列表快了77多倍;遍歷時這種速度差距會更大,所以這是元組一個很神奇的地方,它可以輕松快速地創(chuàng)建,就在于元組避免跟操作系統(tǒng)頻繁打交道,而列表list需要向系統(tǒng)內(nèi)核頻繁申請空間。
我們再看下列表推導式與普通for循環(huán)的性能比較:
我們可以看出,實現(xiàn)同樣的功能執(zhí)行1萬次,列表推導式要比普通for循環(huán)快了4.5秒左右,速度快了2.5倍。所以,推薦大家在以后寫代碼時優(yōu)先使用列表推導式;