Python 1 - 內(nèi)置類(lèi)型 - 序列(1)
Python 中提供了 3 種基本的序列類(lèi)型:list
通孽、tuple
、range
。大家可能對(duì)這3中類(lèi)型都比較熟悉产弹。
一般我們認(rèn)為 tuple
類(lèi)型是不可以改變的list
,當(dāng)然弯囊,這在日常使用中取视,并沒(méi)有什么不對(duì)硝皂,表現(xiàn)出來(lái)的屬性也能驗(yàn)證這個(gè)說(shuō)法,但是作谭,在Python
的底層實(shí)現(xiàn)當(dāng)中稽物,tuple
與 list
是完全不同的兩個(gè)類(lèi)型,后面我們會(huì)對(duì)他們的不同之處加以分析折欠。
語(yǔ)句range(n)
大家在for in
語(yǔ)句中經(jīng)常使用贝或,一般作為index
來(lái)遍歷其他類(lèi)型的迭代。我們?cè)谑褂玫臅r(shí)候一般認(rèn)為range(start, stop, step)
方法產(chǎn)生了一個(gè)以start
開(kāi)始锐秦,stop
結(jié)束咪奖,以step
為間隔的列表。但是實(shí)際上酱床,range
也是一種基本的序列類(lèi)型羊赵,它也并不會(huì)返回一個(gè)我們認(rèn)為的list
。
可變序列
不可變序列類(lèi)型與可變序列的區(qū)別就是扇谣,可變類(lèi)型沒(méi)有實(shí)現(xiàn)對(duì) hash()
內(nèi)置函數(shù)的支持昧捷。這種對(duì)hash()
的支持,可以讓 tuple
作為dict
的鍵存在罐寨。
像list
靡挥,bytearray
就是可變序列。
不可變序列
不可變序列類(lèi)型的對(duì)象一旦創(chuàng)建就不能再改變:如果對(duì)象包含了對(duì)其他對(duì)象的引用鸯绿,其中的可變對(duì)象就是可改變的跋破;但是一個(gè)不可變對(duì)象所直接引用的對(duì)象集是不能改變的。
像 str
,tuple
,bytes
都是不可變對(duì)象瓶蝴。相應(yīng)的不可變序列就有 tuple
和 range
毒返,它們都是可以被hash()
所使用的。
關(guān)于可變對(duì)象和不可變對(duì)象官方給出的例子舷手。
我們打算對(duì)元組的元素進(jìn)行自增運(yùn)算饿悬,所以我們使用了 +=
。
>>> a=(1,2)
>>> a[0] += 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
報(bào)錯(cuò)信息告訴我們聚霜,元組對(duì)象不支持元素賦值狡恬。
發(fā)生異常的原因是顯而易見(jiàn)的:
- 1 會(huì)與對(duì)象 a_tuple[0] 相加,而該對(duì)象為 (1)蝎宇,得到結(jié)果對(duì)象 2弟劲,
- 但當(dāng)我們?cè)噲D將運(yùn)算結(jié)果 2 賦值給元組的 0 號(hào)元素時(shí)就將報(bào)錯(cuò),因?yàn)槲覀儾荒芨淖冊(cè)M的元素所指向的對(duì)象姥芥。
在表層之處兔乞,以上增強(qiáng)賦值語(yǔ)句所做的大致是這樣:
>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
我們打算給元組中的列表元素進(jìn)行擴(kuò)展。
>>> a_tuple = (['foo'], 'bar')
>>> a_tuple[0] += ['item']
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
由于上面的例子,我們知道賦值會(huì)報(bào)錯(cuò)庸追,但是當(dāng)我們查看 a_tuple[0]
時(shí)發(fā)現(xiàn)霍骄,列表已經(jīng)被改變了
>>> a_tuple[0]
['foo', 'item']
要明白為何會(huì)這樣,你需要知道:
- (a) 如果一個(gè)對(duì)象實(shí)現(xiàn)了 iadd 魔術(shù)方法淡溯,它會(huì)在執(zhí)行 += 增強(qiáng)賦值時(shí)被調(diào)用读整,并且其返回值將用于該賦值語(yǔ)句;
- (b) 對(duì)于列表來(lái)說(shuō)咱娶,iadd 等價(jià)于在列表上調(diào)用 extend 并返回該列表米间。
因此對(duì)于列表我們可以說(shuō) += 就是 list.extend 的“快捷方式”:
>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]
>>> result = a_list.__iadd__([1])
>>> a_list = result
a_list 所引用的對(duì)象已被修改,而引用被修改對(duì)象的指針又重新被賦值給 a_list膘侮。
賦值的最終結(jié)果沒(méi)有變化屈糊,因?yàn)樗且?a_list 之前所引用的同一對(duì)象的指針,但仍然發(fā)生了賦值操作
琼了。
因此逻锐,在我們的元組示例中,發(fā)生的事情等同于:
>>>
>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
iadd 成功執(zhí)行雕薪,因此列表得到了擴(kuò)充昧诱,但是雖然 result 指向了 a_tuple[0] 已經(jīng)指向的同一對(duì)象,最后的賦值仍然導(dǎo)致了報(bào)錯(cuò)蹦哼,因?yàn)?code>元組是不可變的鳄哭。
序列的通用方法
所有的序列要糊,都支持以下的方法纲熏,而每個(gè)方法都支持我們?cè)谧远x序列類(lèi)型上實(shí)現(xiàn)這些操作。
in/not in
in 或 not in 是用來(lái)判斷元素是否存在于序列當(dāng)中锄俄。
>>> a = [1,2,3]
>>> 1 in a
True
>>> b = (1,2,3)
>>> 1 in b
True
>>> c = range(10)
>>> 1 in c
True
+
符號(hào) +
是用來(lái)擴(kuò)展序列的局劲。而拼接不可變序列總是會(huì)生成新的對(duì)象。這意味著通過(guò)重復(fù)拼接來(lái)構(gòu)建序列的運(yùn)行時(shí)開(kāi)銷(xiāo)將會(huì)基于序列總長(zhǎng)度的乘方奶赠。如果想降低開(kāi)銷(xiāo)鱼填,只能采用一些其他的方法。
>>> a = [1,2,3]
>>> a1 = [4,5]
>>> a+a1
[1, 2, 3, 4, 5]
操作 +
其實(shí)調(diào)用的是 __add__
或者 __radd__
毅戈。它們接收一個(gè)操作數(shù)苹丸,并根據(jù)是否是可變序列來(lái)返回一個(gè)新的對(duì)象或者更改自己的數(shù)據(jù)。
*
符號(hào)*
既+
的多次自身拼接苇经。上面說(shuō)了 __add__
是用來(lái)實(shí)現(xiàn)擴(kuò)展的赘理,那 *
實(shí)際調(diào)用的就是 __mul__()
或者 __rmul__()
。
>>> a = [1,2,3]
>>> a*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> 3*a
[1, 2, 3, 1, 2, 3, 1, 2, 3]
這里其實(shí)會(huì)有一個(gè)在創(chuàng)建多維列表時(shí)會(huì)出現(xiàn)的問(wèn)題扇单,當(dāng)你嘗試創(chuàng)建一個(gè)二維列表時(shí):[[1],[1],[1],[1]]
>>> a = [[1]] * 4
>>> a
[[1], [1], [1], [1]]
>>> a[0][0] = 2
>>> a
[[2], [2], [2], [2]]
我明明只給 a[0][0]
賦值了商模,為什么整個(gè)列表中其他子列表的值都改變了呢?
這是因?yàn)槭褂?*
創(chuàng)建列表時(shí),執(zhí)行重復(fù)操作并不是創(chuàng)建副本施流,而是創(chuàng)建對(duì)現(xiàn)有對(duì)象的引用响疚。
所以,當(dāng)我們創(chuàng)建多維列表時(shí)瞪醋,使用列表推導(dǎo)式會(huì)更好忿晕。
>>> a = [[1] for i in range(4)]
>>> a
[[1], [1], [1], [1]]
>>> a[0][0] = 2
>>> a
[[2], [1], [1], [1]]
取 [i]
序列都支持從位置 i 處獲取值,i 的范圍是 [0,len(list))趟章。
>>> a=[1,2,3]
>>> a[1]
2
[i] 操作調(diào)用的是 __getitem__(self, key)
方法杏糙,接收的鍵應(yīng)為整數(shù)和切片對(duì)象。
而負(fù)數(shù)索引的特殊解讀是取決于這個(gè)方法的蚓土。
切片操作
通過(guò) range()
的切片操作宏侍,可以很容易的看出它們的關(guān)系。
>>> a = range(10)
>>> a[1:4]
range(1, 4)
>>> a[2:5:3]
range(2, 5, 3)
len, min, max, count
這些函數(shù)都是支持序列操作的蜀漆。 len 既獲取序列數(shù)據(jù)的長(zhǎng)度谅河,min 既獲取最小值,max 既獲取最大值确丢,count 既用來(lái)獲取序列中某個(gè)數(shù)據(jù)的個(gè)數(shù)绷耍。
可變序列類(lèi)型的通用方法
l[i] = x
與取值 [i] 相對(duì)應(yīng)的,可變序列的 [i] 可以被修改賦值鲜侥。實(shí)際上調(diào)用的方法為 __setitem__
褂始。
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> a[0] = 2
>>> a
[2, 2, 3]
l[i:j] = x
與切片 [i:j] 相對(duì)應(yīng)的,切片替換也是一樣存在的描函。 l[i:j] = [i...j]崎苗。
注意:x 必須與它所替換的切片具有相同的長(zhǎng)度,既len(x) == j-i
舀寓。
>>> a = [1,1,1,1,1,1]
>>> a
[1, 1, 1, 1, 1, 1]
>>> a[0:3] = [1,2,3]
>>> a
[1, 2, 3, 1, 1, 1]
del l[i:j]
等同于 l[i:j] = []
>>> a = [1,2,3]
>>> del a[0:1]
>>> a
[2, 3]
l.append(x)
將 x 添加到序列的末尾 (等同于 s[len(s):len(s)] = [x])
>>> a = []
>>> a.append(1)
>>> a
[1]
l.clear()
等同于 del l[:]
>>> a = [1,2,3,4]
>>> a.clear()
>>> a
[]
l.copy()
創(chuàng)建 s 的淺拷貝 (等同于 s[:]
) ,淺拷貝指的是通過(guò) a 的值來(lái)構(gòu)建一個(gè)新的對(duì)象 b胆数,而 b 呢是跟 a 有著相同值得對(duì)象。
>>> a = [1,2,3]
>>> b = a.copy()
>>> b
[1, 2, 3]
>>> id(a)
4504616584
>>> id(b)
4504337160
s.extend(t) 或 s += t
用 t 的內(nèi)容擴(kuò)展 s (基本上等同于 s[len(s):len(s)] = t
)
>>> a = [1]
>>> b = [2,3]
>>> a+=b
>>> a
[1, 2, 3]
>>> a.extend(b)
>>> a
[1, 2, 3, 2, 3]
>>>
s.insert(i, x)
在由 i 給出的索引位置將 x 插入 s (等同于 s[i:i] = [x]
)互墓。
>>> a = [1,2,3]
>>> b = [4,5]
>>> a.insert(0, b)
>>> a
[[4, 5], 1, 2, 3]
>>> a[0:0] = [b]
>>> a
[[4, 5], [4, 5], 1, 2, 3]
s.pop([i])
提取在 i 位置上的項(xiàng)必尼,并將其從 s 中移除,返回被移除的值。
注意:可選參數(shù) i 默認(rèn)為 -1篡撵,因此在默認(rèn)情況下會(huì)移除并返回最后一項(xiàng)判莉。
>>> a
[[4, 5], [4, 5], 1, 2, 3]
>>> a.pop(1)
[4, 5]
>>> c = a.pop(1)
>>> c
1
s.remove(x)
刪除 s 中第一個(gè) s[i] 等于 x 的項(xiàng)目。
>>> a = [1, 2, 1, 2]
>>> a.remove(1)
>>> a
[2, 1, 2]
>>> a.remove(2)
>>> a
[1, 2]
s.reverse()
就地將列表中的元素逆序育谬。當(dāng)反轉(zhuǎn)大尺寸序列時(shí) reverse() 方法會(huì)原地修改該序列以保證空間經(jīng)濟(jì)性券盅。 為提醒用戶(hù)此操作是通過(guò)間接影響進(jìn)行的,它并不會(huì)返回反轉(zhuǎn)后的序列斑司。
>>> a = [1,2,3,4]
>>> a
[1, 2, 3, 4]
>>> a.reverse()
>>> a
[4, 3, 2, 1]
后面的章節(jié)渗饮,將通過(guò)分析每個(gè)序列的使用與源碼但汞,進(jìn)行解讀。序列是 python 中非常重要的數(shù)據(jù)結(jié)構(gòu)互站,了解序列的構(gòu)成對(duì)深入學(xué)習(xí) python 有十分重要的作用私蕾。