「Python3學(xué)習(xí)筆記」讀書筆記—列表

Python 中的 list 類型應(yīng)該是我們平時(shí)用的最多一個(gè)數(shù)據(jù)類型苟穆,如果僅從操作方式上看抄课,列表像是數(shù)組和鏈表的綜合體,除了按索引訪問(wèn)外雳旅,還支持插入、追加间聊、刪除等操作攒盈,完全可以當(dāng)作隊(duì)列或棧來(lái)使用,因此哎榴,如果不考慮性能問(wèn)題型豁,列表是一種易用且功能完善的理想數(shù)據(jù)結(jié)構(gòu)。

列表的內(nèi)部結(jié)構(gòu)由兩部分構(gòu)成:(1) 保存元素?cái)?shù)量和內(nèi)存分配計(jì)數(shù)的頭部尚蝌,(2) 存儲(chǔ)元素指針的獨(dú)立數(shù)組迎变。所有的元素使用該數(shù)組來(lái)保存指針引用,并不嵌入實(shí)際的內(nèi)容飘言。

作為使用頻率最高的數(shù)據(jù)結(jié)構(gòu)之一衣形,列表的性能優(yōu)化很重要。固定長(zhǎng)度的頭部結(jié)構(gòu)姿鸿,很容易實(shí)現(xiàn)內(nèi)存復(fù)用谆吴。創(chuàng)建時(shí),優(yōu)先從復(fù)用區(qū)獲瓤猎ぁ句狼;被回收時(shí),除非超出復(fù)用數(shù)量限制(80)热某,否則會(huì)被放回復(fù)用區(qū)腻菇。每次真正需要分配和釋放的是指針數(shù)組。

用數(shù)組而非鏈表存儲(chǔ)元素項(xiàng)引用昔馋,也有實(shí)際考慮筹吐。從讀操作來(lái)看,無(wú)論是遍歷還是基于序號(hào)訪問(wèn)绒极,數(shù)組的性能總是最高的骏令。盡管插入、刪除等變更操作須移動(dòng)內(nèi)存垄提,但也僅僅是指針復(fù)制榔袋,無(wú)論元素大小周拐,不會(huì)有太高消耗。如果列表太大凰兑,或?qū)懖僮鬟h(yuǎn)多于讀操作妥粟,那么應(yīng)該使用針對(duì)性的數(shù)據(jù)結(jié)構(gòu),而非通用設(shè)計(jì)的內(nèi)置列表類型吏够。

另外勾给,指針數(shù)組內(nèi)存分配算法基于元素?cái)?shù)量和剩余空間大小,按相應(yīng)比率進(jìn)行擴(kuò)容或收縮锅知,非逐項(xiàng)進(jìn)行播急。如此,可以避免太過(guò)頻繁的內(nèi)存分配操作售睹。

構(gòu)建列表

顯示指定元素的構(gòu)建語(yǔ)法最為常用桩警,也可基于類型創(chuàng)建實(shí)例,接收可迭代對(duì)象作為初始內(nèi)容昌妹。不同于數(shù)組捶枢,列表僅存儲(chǔ)指針,而對(duì)元素內(nèi)容并不關(guān)心飞崖,故可以是不同類型混合烂叔。

>>> [1, "abc", 3.14]        # 顯示指定類型
[1, 'abc', 3.14]
>>> list("abc")
['a', 'b', 'c']
>>> list(range(3))
[0, 1, 2]

另有被稱為推導(dǎo)式(comprehension)的語(yǔ)法,同樣使用方括號(hào)固歪,但以 for 循環(huán)初始化元素蒜鸡,并可選用 if 表達(dá)式作為過(guò)濾條件。PS:一旦將推導(dǎo)式兩邊的方括號(hào)改為小括號(hào)昼牛,推導(dǎo)式就變成了生成器术瓮。

>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [i for i in range(10) if i % 2 == 0]
[0, 2, 4, 6, 8]
>>> (i for i in range(10) if i % 2 == 0)        # 生成器
<generator object <genexpr> at 0x102216f10>
>>> l = []

# 推導(dǎo)式的行為等同與以下代碼
>>> l = []
>>> for i in range(10):
...     if i % 2 == 0:
...         l.append(i)
...
>>> l
[0, 2, 4, 6, 8]

專有名詞 ?Pythonic 的基本意思就是寫出簡(jiǎn)潔優(yōu)雅的 Python 代碼,而推導(dǎo)式就算是其中的一種贰健。

無(wú)論是歷史原因胞四,還是實(shí)現(xiàn)方式,內(nèi)置類型關(guān)注性能要多過(guò)設(shè)計(jì)伶椿。如要實(shí)現(xiàn)自定義列表辜伟,書的作者建議基于 collections.UserList 包裝類型來(lái)實(shí)現(xiàn),因?yàn)槌私y(tǒng)一的 collections.abc 體系外脊另,最重要的是該類型重載并完善了相關(guān)運(yùn)算符方法导狡。

>>> list.__bases__
(<class 'object'>,)

>>> import collections
>>> collections.UserList.__bases__
(<class 'collections.abc.MutableSequence'>,)

# 以加法運(yùn)算符為例,繼承不同的類型偎痛,運(yùn)算會(huì)有不同的結(jié)果
>>> class A(list): pass
>>> type(A("abc") + list("de"))
<class 'list'>

>>> class B(collections.UserList): pass
>>> type(B("abc") + list("de"))
<class '__main__.B'>

最小接口設(shè)計(jì)是個(gè)基本原則旱捧,所以應(yīng)該慎重考慮列表這種功能豐富的類型是否適合作為基類。

操作

列表支持用加法連接多個(gè)列表,乘法運(yùn)算符復(fù)制內(nèi)容枚赡。但同是加法(或乘法)運(yùn)算氓癌,但結(jié)果卻不相同。

>>> a = [1, 2]
>>> b = a
>>> a = a + [3, 4]  # 新建列表贫橙,然后與 a 關(guān)聯(lián)
# a贪婉、b 結(jié)果不同,確定 a 指向新對(duì)象
>>> a
[1, 2, 3, 4]
>>> b
[1, 2]

>>> a = [1, 2]
>>> b
[1, 2]
>>> b = a
>>> a += [3, 4]     # 直接修改 a 內(nèi)容
# a卢肃、b 結(jié)果相同疲迂,確認(rèn)修改原對(duì)象
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>> a is b
True

編譯器將 += 運(yùn)算符處理成 INPLACE_ADD 操作,也就是修改原數(shù)據(jù)莫湘,而非新建對(duì)象尤蒿。其效果類似于執(zhí)行 list.extend 方法。PS:+= 運(yùn)算符調(diào)用的是 __iadd__ 方法逊脯,沒(méi)有該方法時(shí)优质,再嘗試調(diào)用 __add__ 方法;+ 運(yùn)算符調(diào)用的是 __add__ 方法军洼,該方法會(huì)返回一個(gè)新的對(duì)象,原對(duì)象不修改演怎。

判斷元素是否存在匕争,習(xí)慣使用 in,而非 index 方法爷耀。

>>> 2 in [1, 2]
True

至于刪除操作甘桑,可用索引序號(hào)指定某個(gè)元素,或切片指定刪除某個(gè)范圍的元素歹叮。

>>> a = [1, 2, 3, 4, 5]
>>> del a[4]        # 刪除單個(gè)元素
>>> a
[1, 2, 3, 4]
>>> del a[1:3]  # 刪除某個(gè)范圍內(nèi)的元素
>>> a
[1, 4]

通過(guò)切片的方式創(chuàng)建新的列表對(duì)象時(shí)跑杭,會(huì)復(fù)制相關(guān)的指針數(shù)據(jù)到新數(shù)組。除了所引用的目標(biāo)相同外咆耿,對(duì)列表自身的修改(插入德谅、刪除等)互不影響。

>>> a = [0, 2, 4, 6]
>>> b = a[:2]
>>> a[0] is b[0]        # 復(fù)制引用萨螺,依然指向同一對(duì)象
True
>>> a.insert(1, 1)  # 對(duì) a 列表操作窄做,不會(huì)影響 b
>>> a
[0, 1, 2, 4, 6]
>>> b
[0, 2]

復(fù)制的是指針(引用),而非目標(biāo)元素對(duì)象慰技。
對(duì)列表自身的修改互不影響椭盏,但對(duì)目標(biāo)元素對(duì)象的修改是共享的。

利用 bisect 模塊吻商,可向有序列表插入元素掏颊。它使用二分法查找適合的位置,可以用來(lái)實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列或一致性哈希算法艾帐。

>>> d = [0, 2, 4]
>>> import bisect
>>> bisect.insort_left(d, 1)    # 插入新元素后乌叶,依然保持有序狀態(tài)
>>> d
[0, 1, 2, 4]
>>> bisect.insort_right(d, 3)
>>> d
[0, 1, 2, 3, 4]

自定義復(fù)合類型盆偿,可通過(guò)重載比較運(yùn)算符( __eq__、__lt__ 等)實(shí)現(xiàn)自定義排序條件枉昏。

元組

盡管兩者并沒(méi)有直接關(guān)系陈肛,但在操作方式上,元組可被當(dāng)作列表的只讀版本使用兄裂。

因元組是不可變類型句旱,它的指針數(shù)組無(wú)須變動(dòng),故一次性完成內(nèi)存分配晰奖。系統(tǒng)會(huì)緩存復(fù)用一定長(zhǎng)度的元組內(nèi)存(含指針數(shù)組)谈撒。創(chuàng)建時(shí),按所需長(zhǎng)度提取復(fù)用匾南,沒(méi)有額外的內(nèi)存分配啃匿。從這一點(diǎn)上來(lái)看,元組的性能要好于列表蛆楞。

Python 3.6 緩存復(fù)用長(zhǎng)度在 20 以內(nèi)的 tuple 內(nèi)存溯乒,每種 2000 上限。

# IPyton
>>> %timeit [1, 2, 3]
53.7 ns ± 1.49 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

>>> %timeit (1, 2, 3)
12.9 ns ± 0.027 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

元組支持與列表類似的運(yùn)算符操作豹爹,但不能修改裆悄,每次操作都是返回新的對(duì)象。

>>> a = [1, 2, 3]
>>> id(a)
4529921800
>>> a += [4, 5]
>>> id(a)
4529921800

>>> b = (1, 2, 3)
>>> id(b)
4537321872
>>> b += (4, 5)
>>> id(b)
4537253648

列表因?yàn)橹С植迦氡哿h除等修改操作光稼,所以序號(hào)無(wú)法與元素對(duì)象構(gòu)成固定映射。但元組不同孩等,相同序號(hào)總是返回同一對(duì)象艾君,故可為序號(hào)取個(gè)“別名”(namedtuple)。

>>> import collections
>>> User = collections.namedtuple("User", "name,age")   # 創(chuàng)建 User 類型肄方,指定字段
>>> issubclass(User, tuple)     # tuple 子類
True
>>> user = User("Min", 22)
>>> user.name, user.age     # 使用字段名訪問(wèn)
('Min', 22)
>>> user[0] is user.name        # 或依舊使用序號(hào)
True

對(duì)于自定義純數(shù)據(jù)類型冰垄,顯然 namedtuple 要比 class 簡(jiǎn)介。關(guān)鍵在于扒秸,名字要比序號(hào)可讀性更強(qiáng)并且更易維護(hù)播演,其類似于數(shù)字常量維護(hù)。

數(shù)組

數(shù)組與列表伴奥、元組的本質(zhì)區(qū)別在于:元素單一類型和內(nèi)容嵌入写烤。

>>> import array
>>> a = array.array("b", [0x11, 0x22, 0x33, 0x44])
>>> memoryview(a).hex()     # 使用內(nèi)存視圖查看,內(nèi)容嵌入而非指針
'11223344'

>>> a = array.array("i")
>>> a.append(100)
>>> a.append(1.23)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: integer argument expected, got float

數(shù)組可直接存儲(chǔ)包括 Unicode 字符在內(nèi)的各種數(shù)字拾徙。

但復(fù)合類型須用 struct洲炊、marshal、pickle 等轉(zhuǎn)換為二進(jìn)制字節(jié)后再進(jìn)行存儲(chǔ)。

數(shù)組于列表類似暂衡,長(zhǎng)度不固定询微,按需擴(kuò)張或收縮內(nèi)存。

>>> a = array.array("i", [1, 2, 3])
>>> a.buffer_info()     # 返回緩沖區(qū)的內(nèi)存地址和長(zhǎng)度
(4480106800, 3)
>>> a.extend(range(100000))     # 追加大量?jī)?nèi)容后狂巢,內(nèi)存地址和長(zhǎng)度發(fā)生變化
>>> a.buffer_info()
(4487446528, 100003)

由于可指定更緊湊的數(shù)字類型撑毛,故數(shù)組可節(jié)約更多內(nèi)存。再者唧领,內(nèi)容嵌入也避免了對(duì)象的額外開(kāi)銷藻雌,減少了活躍對(duì)象的數(shù)量和內(nèi)存分配的次數(shù)。

從博客搬運(yùn)到簡(jiǎn)書:原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斩个,一起剝皮案震驚了整個(gè)濱河市胯杭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌受啥,老刑警劉巖做个,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滚局,居然都是意外死亡居暖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門藤肢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膝但,“玉大人,你說(shuō)我怎么就攤上這事谤草。” “怎么了莺奸?”我有些...
    開(kāi)封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵丑孩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我灭贷,道長(zhǎng)温学,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任甚疟,我火速辦了婚禮仗岖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘览妖。我一直安慰自己轧拄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布讽膏。 她就那樣靜靜地躺著檩电,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俐末,一...
    開(kāi)封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天料按,我揣著相機(jī)與錄音,去河邊找鬼卓箫。 笑死载矿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烹卒。 我是一名探鬼主播闷盔,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼甫题!你這毒婦竟也來(lái)了馁筐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坠非,失蹤者是張志新(化名)和其女友劉穎敏沉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炎码,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盟迟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潦闲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攒菠。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖歉闰,靈堂內(nèi)的尸體忽然破棺而出辖众,到底是詐尸還是另有隱情,我是刑警寧澤和敬,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布凹炸,位于F島的核電站,受9級(jí)特大地震影響昼弟,放射性物質(zhì)發(fā)生泄漏啤它。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一舱痘、第九天 我趴在偏房一處隱蔽的房頂上張望变骡。 院中可真熱鬧,春花似錦芭逝、人聲如沸塌碌。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)誊爹。三九已至蹬刷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間频丘,已是汗流浹背办成。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搂漠,地道東北人迂卢。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桐汤,于是被迫代替她去往敵國(guó)和親而克。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容