數(shù)據(jù)類型之元組和列表的性能分析、命名元組
目錄
- 一、元組和列表
- 1.元組和列表的性能分析
- 2.為什么列表在 Python 中是最常用的呢?
- 3.timeit 里面有個 Timer 類
- 4.timeit 里面還有個直接用的 timeit 的方法,timeit.timeit()
- 5.這 2 個方法有啥區(qū)別?
- 二、命名元組
- 三、命名元組有什么特點?
一、元組和列表
元組vs列表
1.元組和列表的性能分析
元組和列表用來存儲數(shù)據(jù),在元組和列表里面查詢的時候,到底哪個更快呢?
計算創(chuàng)建元組和列表所需的時間:ipython 中使用timeit這個命令。
計算時間模塊介紹:
- import timeit
- #timeit.timeit
可以用這個模塊來測試函數(shù)的性能。
安裝 ipython:pip install ipython
ipython 是個交互環(huán)境,就跟我們輸入 Python 進去是一樣的。只不過它外面做了一層封裝,比 Python 交互環(huán)境更好用一點。
ipython 里面有一個命令叫做timeit,后面可以跟一個 Python 表達式。
例如定義一個列表在后面:
敲完這行命令,返回了一行時間。這個是在內(nèi)存中初始化一個列表,如圖可以看到創(chuàng)建了一千萬次,時間是 48.4ns
可以看出,創(chuàng)建一個元組比創(chuàng)建一個列表要快得多。
元組的速度比列表要快 3 倍多。在內(nèi)存里,當我們創(chuàng)建一個列表的時候,會劃分一塊區(qū)域出來,拿一塊區(qū)域給列表來儲存值。例如初始化,里面給它留了 20 個位置在這個列表里面儲存值。
列表占用內(nèi)存如圖
當儲存到一定程度,Python 解釋器檢測到列表快要盛滿了的時候,它會對列表做一個擴容。
給擴容到 200,當存儲到 150 的時候,發(fā)現(xiàn)又快存儲滿了,又會給你繼續(xù)擴容。
隨著數(shù)據(jù)的增多,底層會不斷給這個列表擴容。
初始化一個元組,同樣也是一千萬次,只需 12.8ns
元組是一個不可變的類型。
比如定義的元組里面有 3 個元素,Python 解釋器在給它分內(nèi)存的時候,就給它分了 3 個格子。
這里面只能存 3 條數(shù)據(jù),就這么大,所以元組占用的內(nèi)存比列表要少。
元組和列表內(nèi)存占用對比圖
用一個列表存儲 50 條數(shù)據(jù)和用一個元組存儲 50 條數(shù)據(jù),那么元組占用的內(nèi)存要比列表小得多。
2.為什么列表在 Python 中是最常用的呢?
因為列表比較靈活,用列表的話,可以往里面不斷得添加元素。如果元素固定的,那就用元組。
3.timeit 里面有個 Timer 類。
來看看這個類的源碼:
timer=default_timer代表的是:創(chuàng)建一個列表、元組等,它要執(zhí)行的一個次數(shù)。
看源碼,默認是一千萬次:
import timeit # 這個模塊可以用來做性能分析def func(): for i in range(10): print(i)# 這個對象有個方法叫做timeitres = timeit.Timer(func).timeit(100) # 把這個func函數(shù)傳進去,運行100次,然后返回的是個時間# timeit.Timer(func).timeit(100)中函數(shù)func是不需要加引號的,如果是字符串、列表這些需要加# 引號放進去print(res)
可以看到運行 100 次需要的時間是:0.0043269999999999975
4.timeit 里面還有個直接用的 timeit 的方法,timeit.timeit()
- import timeit # 這個模塊可以用來做性能分析
- def func():
- for i in range(10):
- print(i)
- # 這個對象有個方法叫做timeit
- # res = timeit.Timer(func).timeit(100) # 把這個func函數(shù)傳進去,運行100次,然后返回的是個時間
- # timeit.Timer(func).timeit(100)中函數(shù)func是不需要加引號的,如果是字符串、列表這些需要加引號放進去
- # print(res)
- res2 = timeit.timeit('[1,2,3]')
- print(res2)
這個模塊的作用: 大家寫的功能函數(shù),可以用它測下功能函數(shù)的速度,執(zhí)行大概要多久。
默認是一千萬次,結(jié)果如下:
如果列表不加引號直接傳是會報錯的:
提示不可被調(diào)用!
5.這 2 個方法有啥區(qū)別?
其實它們是一個東西。
二、命名元組
元組的性能是大大優(yōu)于列表的。元組、列表在使用的時候,都是通過下標索引取值的。
下標索引取值不太人性化,如果我知道數(shù)據(jù)儲存在元組里面,但是我不知道它具體儲存的下標位置。這個時候找這個元素,還得先把下標找出來,知道下標再去拿,這樣很不方便。
字典的話,這方面就比較有優(yōu)勢。數(shù)據(jù)是存儲在字典里面的,只要通過鍵,就能把值找到。字典相對于元組和列表,有一定的優(yōu)勢和劣勢。
命名元組使用的時候可以讓元組像字典一樣去取值。
例如,有個元組里面保存了 3 條數(shù)據(jù):
創(chuàng)建一個命名元組的話,需要用到 Python 的一個內(nèi)置模塊from collections import namedtuple
- import timeit # 這個模塊可以用來做性能分析
- from collections import namedtuple
- # namedtuple是個函數(shù),創(chuàng)建命名元組可以通過這個函數(shù)來創(chuàng)建
- def func():
- for i in range(10):
- print(i)
- # 這個對象有個方法叫做timeit
- # res = timeit.Timer(func).timeit(100) # 把這個func函數(shù)傳進去,運行100次,然后返回的是個時間
- # timeit.Timer(func).timeit(100)中函數(shù)func是不需要加引號的,如果是字符串、列表這些需要加引號放進去
- # print(res)
- res2 = timeit.timeit('[1,2,3]')
- print(res2)
- # 命名元組
- # 如果知道里面儲存的具體位置,可以通過下標取值。例如tu=[0]
- # 如果我不知道名字存儲在哪里,通過下標去取值就不好取了
- # 命名元組可以使取值的時候像列表一樣取
- student_info = namedtuple('info_tuple',['name','age','gender']) # 這個函數(shù)接收2個參數(shù),第一個參數(shù)是創(chuàng)建命名元組的類型的名字;
- # 第二個參數(shù)的話,傳一個列表
- # 列表里寫創(chuàng)建命名元組的一個命名,例如第一個元素命名為name
- # 這個函數(shù)調(diào)用傳了2個參數(shù),返回出來一個對象。這個對象叫做student_info
- # 通過這個對象student_info創(chuàng)建命名元組
- tu = student_info('qinghan',18,'nv')
- print(tu)
這個 tu 就是個命名元組。
student_info 是通過命名元組這個namedtuple函數(shù)創(chuàng)建命名元組類型:namedtuple('info_tuple',['name','age','gender'])。
然后返回出來一個對象student_info
通過student_info這個對象傳入對應的元組,定義元組的時候就通過這個對象把元素寫進去,返回的就是命名元組。
三、命名元組有什么特點?
它取值的時候可以像字典一樣取值,通過對應的鍵,找到對應的值。命名元組使用起來更像對象。
這樣用:命名元組.name
這樣就能找到 name 所對應的值:
- import timeit # 這個模塊可以用來做性能分析
- from collections import namedtuple
- # namedtuple是個函數(shù),創(chuàng)建命名元組可以通過這個函數(shù)來創(chuàng)建
- def func():
- for i in range(10):
- print(i)
- # 這個對象有個方法叫做timeit
- # res = timeit.Timer(func).timeit(100) # 把這個func函數(shù)傳進去,運行100次,然后返回的是個時間
- # timeit.Timer(func).timeit(100)中函數(shù)func是不需要加引號的,如果是字符串、列表這些需要加引號放進去
- # print(res)
- res2 = timeit.timeit('[1,2,3]')
- print(res2)
- # 命名元組
- # 如果知道里面儲存的具體位置,可以通過下標取值。例如tu=[0]
- # 如果我不知道名字存儲在哪里,通過下標去取值就不好取了
- # 命名元組可以使取值的時候像列表一樣取
- student_info = namedtuple('info_tuple',['name','age','gender']) # 這個函數(shù)接收2個參數(shù),第一個參數(shù)是創(chuàng)建命名元組的類型的名字;
- # 第二個參數(shù)的話,傳一個列表
- # 列表里寫創(chuàng)建命名元組的一個命名,例如第一個元素命名為name
- # 這個函數(shù)調(diào)用傳了2個參數(shù),返回出來一個對象。這個對象叫做student_info
- # 通過這個對象student_info創(chuàng)建命名元組
- tu = student_info('qinghan',18,'nv')
- print(tu.name)
設定命名元組類型的時候,它返回的這個對象它里面只包含了傳進去的這幾個名字。
接下來,要創(chuàng)建命名元組的時候,元素和它一樣多,名字和對應的元素的值是一一對應的,不能多,不能少。
否則就會報錯:
print(type(tu)) # 看下它的類型
它返回的對象和類型名用的同一個名字。
print(type(student_info))
- import timeit # 這個模塊可以用來做性能分析
- from collections import namedtuple
- # namedtuple是個函數(shù),創(chuàng)建命名元組可以通過這個函數(shù)來創(chuàng)建
- def func():
- for i in range(10):
- print(i)
- # 這個對象有個方法叫做timeit
- # res = timeit.Timer(func).timeit(100) # 把這個func函數(shù)傳進去,運行100次,然后返回的是個時間
- # timeit.Timer(func).timeit(100)中函數(shù)func是不需要加引號的,如果是字符串、列表這些需要加引號放進去
- # print(res)
- res2 = timeit.timeit('[1,2,3]')
- print(res2)
- # 命名元組
- # 如果知道里面儲存的具體位置,可以通過下標取值。例如tu=[0]
- # 如果我不知道名字存儲在哪里,通過下標去取值就不好取了
- # 命名元組可以使取值的時候像列表一樣取
- # 設定命名元組類型
- # student_info是個類
- student_info = namedtuple('student_info',['name','age','gender']) # 這個函數(shù)接收2個參數(shù),第一個參數(shù)是創(chuàng)建命名元組的類型的名字;
- # 第二個參數(shù)的話,傳一個列表
- # 列表里寫創(chuàng)建命名元組的一個命名,例如第一個元素命名為name
- # 這個函數(shù)調(diào)用傳了2個參數(shù),返回出來一個對象。這個對象叫做student_info
- # 通過這個對象student_info創(chuàng)建命名元組
- tu = student_info('qinghan',18,'nv')
- print(tu.name)
- print(type(tu)) # 看下它的類型
- print(type(student_info))
- # 因為student_info是個類,所以看student_info的type就是個type。隨便看哪個類都是一樣的。
本文轉(zhuǎn)載自微信公眾號「清菡軟件測試」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系清菡軟件測試公眾號。