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