引出
在使用Python過程中,列表、集合和字典是比較常用的數據結構。
-
列表簡單說就是數組,不對,它就是數組
-
集合就是去重的元素結構,和JAVA中的set一樣
-
字典就是一個key-value的鍵值對,和JAVA中的HashTable一樣
但是,Python中有一個特立獨行的對象,元組tuple
,看一個元組的簡單使用:
tu = (2, 3) a = tu[0] # a=2 b = tu[1] # b=3
什么?你告訴我這個一個新的結構?不是數組???
這用起來跟數組也沒什么區別啊?
要看元組和數組的區別,最直觀的比較,就是比較兩個結構的方法,通過方法來理解結果。
方法比較
列表用的比較多了,方法基本上都是常規的數組操作:對數組的增刪改查。對了,還有Python列表最屌的操作,數組的切片操作。
(悄悄告訴你,查看方法只要Python運行 help(list)
, 就可以了)
再看一下元組的方法,暴露出來的方法只有兩個,count
和index
-
count(x)
: 統計x在元組中的個數 -
index(x)
: 返回x在元組中第一次出現的索引
恩,我知道區別了,元組只能查,不能做增刪改的操作。
只能查詢,不可修改,這不是常量么。。。。既然是常量,想必虛擬機內部會做優化,元組占用的空間會比列表少很多吧。
內存比較
分別定義列表和元組,查看其內存占用情況:
from sys import getsizeof if __name__ == '__main__': tu = (x for x in range(20000)) li = [x for x in range(20000)] print(getsizeof(tu)) print(getsizeof(li))
輸出結果:
啥?元組這么小么?我兩萬個數字才占用88個字節?我不服,再怎么優化這也不可能,它不是元組:
if __name__ == '__main__': tu = (x for x in range(20000)) li = [x for x in range(20000)] print(type(tu)) print(type(li))
哦哦,不好意思啊,走錯片場了,這是個生成器。重新來過:
from sys import getsizeof
if __name__ == '__main__': tu = tuple(x for x in range(20000)) li = list(x for x in range(20000)) print(type(tu)) print(getsizeof(tu)) print(type(li)) print(getsizeof(li))
這回沒毛病了,元組確實比列表占用空間要少一些。
至此,基本已經確定了,元組最大的特性就是不可變。
通過元組的不可變特性,引申出了很多數組無法實現的功能
這里,看到網上有人說元組中的數組是可變的,也給出了對應的解釋。簡單說,元組中保存的是數組的地址,盡管數組內容變了,但地址沒有變,也就是元組內容沒有發生變化,很好理解。
元組的靈活使用
-
元組是可以計算hash值的,這也就意味元組可以當做hashTable中的key存在
if __name__ == '__main__': tu = tuple(x for x in range(20000)) li = list(x for x in range(20000)) print(hash(tu)) print(hash(li))
有人說,字符串就足夠了,沒必要用元組。恩?我想到一個應用場景:
如果要通過用戶的信息(身高,體重,性別)來查找用戶的id,我們固然可以遍歷一遍用戶,將符合條件的篩選出來。但這樣太慢了,如果我們維護一個用戶信息為key,值為id數組的hashMap,那查找就十份快速了。
當然,使用字符串也完全可以滿足,將用戶的各種信息拼接起來,但使用元組顯然更加直觀,key直接就是(身高,體重,性別)。
-
這個雖然和元組的不可變沒什么關聯,但同樣十分實用。實現函數返回多個值。
def test_fun(): return 2, 3 if __name__ == '__main__': a, b = test_fun() # 用*來接受剩余的內容 d, *other = test_fun() re = test_fun() print(a, b) print(re) print(type(re))
媽媽再也不用擔心我的函數返回了。
站在巨人的肩膀上
通過先人的成果來理解列表和元組,下面以numpy為例,通過作者對二者的理解來幫助我理解。
import numpy if __name__ == '__main__': # 創建一個二維數組 a = numpy.arange(9).reshape(3, 3) print(a) tu = (1, 2) li = [1, 2] print(a[tu]) print(a[li])
顯然,使用元組訪問時,它接收到的意圖是:我想要下標為1的數組中下標為2的元素。而使用數組訪問時,它收到的意圖是:請把下標為1和下標為2的元素給我。在此,意會一下。