- 零基礎入門學習Python(第2版)
- 小甲魚
- 4088字
- 2019-12-20 12:19:37
第5章 列表、元組和字符串
5.1 列表:一個“打了激素”的數組

視頻講解
有時候可能需要將一些相互之間有關聯的數據保存到一起,很多接觸過編程的讀者腦海里浮現出來的第一個概念應該就是數組。數組允許把一些相同類型的數據挨個兒擺在一起,然后通過下標進行索引。
Python也有類似數組的東西,不過更為強大。由于Python的變量沒有數據類型,所以Python的“數組”可以同時存放不同類型的變量。這么厲害的東西,Python將其稱為列表,姑且可以認為列表就是一個“打了激素”的數組。
5.1.1 創建列表
創建一個列表非常簡單,只需要使用中括號將數據包裹起來(數據之間用逗號分隔)就可以了。
>>> [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
上面創建了一個匿名的列表,因為沒有名字,所以創建完也沒辦法再次使用它。為了可以隨時對它進行引用和修改,可以給它貼上一個變量名:

注意:
type()函數用于返回指定參數的類型,list即列表的意思。
沒有哪一項規定要求Python的列表保存同一類型的數據,因此,它支持將各種不同的數據存放到一起:
>>> mix = [520, "小甲魚", 3.14, [1, 2, 3]]
可以看到這個列表里有整型、字符串、浮點型數據,甚至還可以包含另一個列表。
當實在想不到要往列表里面塞什么數據的時候,可以先創建一個空列表:
>>> empty = []
5.1.2 向列表添加元素
列表并不是一成不變的,可以隨意地往里面添加新的元素。添加元素到列表中,可以使用append()方法:
>>> number = [1, 2, 3, 4, 5] >>> number.append(6) >>> number [1, 2, 3, 4, 5, 6]
可以看到,數字6已經被添加到列表number的末尾了。有讀者可能會問,這個append()的調用怎么跟平時的BIF內置函數調用不一樣呢?
因為append()并不是一個BIF,它是屬于列表對象的一個方法。中間這個“.”,暫時可以理解為范圍的意思:append()這個方法是屬于一個叫number的列表對象的。關于對象的知識,暫時只需要理解這么多,后面小甲魚會再詳細地來介紹對象。
下面代碼試圖將數字8和9同時添加進number列表中:

出錯了,這是因為append()方法只支持一個參數。
如果希望同時添加多個數據,可以使用extend()方法向列表末尾添加多個元素:
>>> number.extend([8, 9]) >>> number [1, 2, 3, 4, 5, 6, 8, 9]
注意:
extend()事實上是使用一個列表來擴充另一個列表,所以它的參數是另一個列表。
無論是append()還是extend()方法,都是往列表的末尾添加數據,那么是否可以將數據插入到指定的位置呢?
當然沒問題,想要往列表的任意位置插入元素,可以使用到insert()方法。
insert()方法有兩個參數:第一個參數指定待插入的位置(索引值),第二個參數是待插入的元素值。
下面代碼將數字0插入到number列表的最前面:
>>> number.insert(0, 0) >>> number [0, 1, 2, 3, 4, 5, 6, 8, 9]
在計算機編程中常常會出現一些“反常識”的知識點,如在Python列表中,第一個位置的索引值是0,第二個是1,第三個是2,以此類推……
下面代碼將數字7插入到6和8之間:
>>> number.insert(7, 7) >>> number [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
insert()方法中代表位置的第一個參數還支持負數,表示與列表末尾的相對距離:
>>> number.insert(-1, 8.5) >>> number [0, 1, 2, 3, 4, 5, 6, 7, 8, 8.5, 9]
5.1.3 從列表中獲取元素

視頻講解
通過索引值可以直接獲取列表中的某個元素:
>>> eggs = ["雞蛋", "鴨蛋", "鵝蛋", "鐵蛋"] >>> eggs[0] '雞蛋' >>> eggs[3] '鐵蛋'
如果想要訪問列表中最后一個元素,怎么辦?可以使用len()函數獲取該列表的長度(元素個數),再減1就是這個列表最后一個元素的索引值:
>>> eggs = ["雞蛋", "鴨蛋", "鵝蛋", "鐵蛋"] >>> eggs[len(eggs)-1] '鐵蛋' >>> eggs[len(eggs)-2] '鵝蛋'
len()函數的調用直接省去也可以實現同樣的效果,即當索引值為負數時,表示從列表的末尾反向索引:

如果要將“鴨蛋”和“鐵蛋”的位置進行調換,通常可以這么寫:

這里的temp是一個臨時變量,避免相互覆蓋。不過Python允許適當地“偷懶”,下面代碼可以實現相同的功能:

有時候可能需要開發一個具有“抽獎”功能的程序,只需要先將“獎項/參與者”放到列表里面,然后配合random模塊即可實現:

random的choice()方法可以從一個非空的序列(如列表)中隨機獲取一個元素。
列表中還可以包含另一個列表,如果要獲取內部子列表的某個元素,應該使用兩次索引:

5.1.4 從列表刪除元素
從列表中刪除元素,可以有三種方法實現:remove()、pop()和del。
remove()方法需要指定一個待刪除的元素:

使用remove()刪除元素,并不需要知道這個元素在列表中的具體位置。但是如果指定的元素不存在于列表中,程序就會報錯:

pop()方法是將列表中的指定元素“彈”出來,也就是取出并刪除該元素的意思,它的參數是一個索引值:

如果不帶參數,pop()方法默認是彈出列表中的最后一個元素:

最后一個是del語句,注意,它是一個Python語句,而不是del列表的方法,或者BIF:

del語句在Python中的用法非常豐富,不僅可以用來刪除列表中的某個(些)元素,還可以直接刪除整個變量:

分析:上面代碼由于eggs整個變量被del語句刪除了,所以再次引用時,Python由于找不到該變量,便會報錯。
5.1.5 列表切片
切片(slice)語法的引入,使得Python的列表真正地走向了高端。這個連Python之父都愛不釋手的語法真有那么神奇嗎?不妨來試一試。
現在要求將列表list1中的三個元素取出來,放到列表list2里面。學了前面的知識,可以使用“笨”方法來實現:

像這樣,從一個列表中取出部分元素是非常常見的操作,但這里是取出三個元素,如果要求取出列表中最后200個元素,那不是很心酸?
其實動動腦筋還是可以實現的:

雖然可以實現,但是每次都要套個循環跑一圈,未免也太煩瑣了!切片的引入,大大地簡化了這種操作:

很簡單對吧?只不過是用一個冒號隔開兩個索引值,左邊是開始位置,右邊是結束位置。這里要注意的一點是:結束位置上的元素是不包含的(如上面例子中,“神奇女俠”的索引值是4,如果寫成list1[2:4],便不能將其包含進來)。
使用列表切片也可以“偷懶”,之前提到過Python是以簡潔而聞名于世,所以你能想到的“便捷方案”,Python的作者以及Python社區的小伙伴們都已經想到了,并付諸實踐,你要做的就是驗證一下是否可行:

如果省略了開始位置,Python會從0這個位置開始。同樣道理,如果要得到從指定索引值到列表末尾的所有元素,把結束位置也省去即可。如果啥都沒有,只有一個冒號,Python將返回整個列表的拷貝。
這種方法有時候非常方便,如想獲取列表最后的幾個元素,可以這么寫:

注意:
列表切片并不會修改列表自身的組成結構和數據,它其實是為列表創建一個新的拷貝(副本)并返回。
5.1.6 進階玩法
列表切片操作實際上還可以接受第三個參數,其代表的是步長,默認值為1。下面將步長修改為2,看看有什么神奇的效果?

其實該代碼還可以直接寫成list1[::2],實現效果是一樣的。
如果將步長設置為負數,如-1,結果會是怎樣呢?
>>> list1[::-1] [9, 8, 7, 6, 5, 4, 3, 2, 1]
這就很有意思了,將步長設置為-1,相當于將整個列表翻轉過來。
上面這些列表切片操作都是獲取列表加工后(切片)的拷貝,并不會影響到原有列表的結構:

但如果將del語句作用于列表切片,其結果又讓人大跌眼鏡:
>>> del list1[::2] >>> list1 [2, 4, 6, 8]
是的,del直接作用于原始列表了,因為不這樣做的話,代碼就失去意義了,不是嗎?同樣會作用于原始列表的操作還有為切片后的列表賦值:
>>> list1 = ["鋼鐵俠", "蜘蛛俠", "蝙蝠俠", "綠燈俠", "神奇女俠"] >>> list1[0:2] = ["超人", "閃電俠"] >>> list1 ['超人', '閃電俠', '蝙蝠俠', '綠燈俠', '神奇女俠']
5.1.7 一些常用操作符

視頻講解
此前學過的大多數操作符都可以運用到列表上:
>>> list1 = [123] >>> list2 = [234] >>> list1 > list2 False >>> list1 <= list2 True >>> list3 = ['apple'] >>> list4 = ['pineapple'] >>> list3 < list4 True
列表好像挺聰明的,不僅懂得比大小,還知道菠蘿(pineapple)比蘋果(apple)大?那如果列表中不止一個元素呢?結果又會如何?
>>> list1 = [123, 456] >>> list2 = [234, 123] >>> list1 > list2 False
怎么會這樣?Python做出這樣的判斷是基于什么根據呢?總不會是隨機瞎猜的吧?
list1列表兩個元素的和是579,按理應該比list2列表的和357要大,那為什么list1>list2還會返回False呢?
其實,Python的列表原來并沒有我們想象中那么“智能”,當列表包含多個元素的時候,默認是從第一個元素開始比較,只要有一個PK贏了,就算整個列表贏了。字符串比較也是同樣的道理(字符串比較的是每一個字符對應的ASCII碼值的大小)。
前面演示過字符串可以使用加號(+)進行拼接,使用乘號(*)來實現自我復制。這兩個操作符也可以作用于列表:
>>> list1 = [123, 456] >>> list2 = [234, 123] >>> list3 = list1 + list2 >>> list3 [123, 456, 234, 123]
加號(+)也叫連接操作符,它允許把多個列表對象合并在一起,其實就相當于extend()方法實現的效果。一般情況下建議使用extend()方法來擴展列表,因為這樣顯得更為規范和專業。另外,連接操作符并不能實現列表添加新元素的操作:

乘號(*)也叫重復操作符,重復操作符同樣可以用于列表中:
>>> list1 = ["FishC"] >>> list1 * 3 ['FishC', 'FishC', 'FishC']
另外有個成員關系操作符大家也不陌生了,我們是在談for循環的時候認識它的,成員關系操作符就是in和not in:
>>> list1 = ["小豬", "小貓", "小狗", "小甲魚"] >>> "小甲魚" in list1 True >>> "小烏龜" not in list1 True
之前說過列表里邊可以包含另一個列表,那么對于列表中的列表的元素,能不能使用in和not in測試呢?試試便知:
>>> list1 = ["小豬", "小貓", ["小甲魚", "小烏龜"], "小狗"] >>> "小甲魚" in list1 False >>> "小烏龜" not in list1 True
可見in和not in只能判斷一個層次的成員關系,這跟break和continue語句只能跳出一個層次的循環是一個道理。
在開發中,有時候需要去除列表中重復的數據,只要利用好in和not in,就可以巧妙地實現:

分析:代碼先迭代遍歷old_list的每一個元素,如果該元素不存在于new_list中,便調用列表的append()方法添加進去。
5.1.8 列表的小伙伴們
接下來認識一下列表的小伙伴們,列表有多少小伙伴呢?不妨讓Python自己告訴我們:

產生了一個熟悉又陌生的列表,很多熟悉的方法似曾相識,如append()、extend()、insert()、pop()、remove()都是學過的。下面再給大家介紹幾個常用的方法。
count()方法的作用是統計某個元素在列表中出現的次數:
>>> list1 = [1, 1, 2, 3, 5, 8, 13, 21] >>> list1.count(1) 2
index()方法的作用是返回某個元素在列表中第一次出現的索引值:
>>> list1.index(1) 0
index()方法可以限定查找的范圍:
>>> start = list1.index(1) + 1 >>> stop = len(list1) >>> list1.index(1, start, stop) 1
reverse()方法的作用是將整個列表原地翻轉:
>>> list1 = [1, 1, 2, 3, 5, 8, 13, 21] >>> list1.reverse() >>> list1 [21, 13, 8, 5, 3, 2, 1, 1]
sort()方法的作用是對列表元素進行排序:
>>> list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0] >>> list1.sort() >>> list1 [0, 1, 2, 3, 5, 6, 8, 9, 10]
那如果需要從大到小排隊呢?很簡單,先調用sort()方法,列表會先從小到大排好隊,然后調用reverse()方法原地翻轉就可以啦。
什么?太麻煩?好吧,大家真是越來越懶了……很好,“懶”有時候確實是發明創新的原動力。其實,sort()這個方法有三個參數,語法形式為:
sort(func, key, reverse)
func和key參數用于設置排序的算法和關鍵字,默認是使用歸并排序,算法問題不在這里討論,感興趣的朋友可以參考小甲魚的另一部視頻教程——《數據結構和算法》。這里討論sort()方法的第三個參數:reverse,沒錯,就是剛剛學的那個reverse()方法的reverse。不過這里作為sort()的一個默認參數,它的默認值是sort(reverse=False),表示不顛倒順序。因此,只需要把False改為True,列表就相當于從大到小排序:
>>> list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0] >>> list1.sort(reverse=True) >>> list1 [10, 9, 8, 6, 5, 3, 2, 1, 0]