作者 | 弗拉德
來源 | 弗拉德(公眾號:fulade_me)
在上一節(jié)我們學習了如何創(chuàng)建一個列表,在列表里面插入、刪除數(shù)據(jù)等操作。
本節(jié)我們學習如何管理列表。
遍歷列表
在日常開發(fā)中,我們經(jīng)常需要遍歷列表的所有元素,對每個元素執(zhí)行相同的操作。例如,在管理商場的蔬菜時候,需要給所有的蔬菜商品都打7折,并重新生成價格。當我們需要對列表中的每個元素都執(zhí)行相同的操作時,可使用Python中的for循環(huán)。
假設我們有一個蔬菜名單,需要將其中每種蔬菜的名字都打印出來。為此,我們可以采用元素下標的方式分別獲取名單中的每個名字,但這種做法會導致多個問題。例如,如果名單很長,將包含大量重復的代碼。另外,每當名單的長度發(fā)生變化時,都必須修改代碼。通過使用for循環(huán),可讓Python去處理這些問題。
下面使用for循環(huán)來打印蔬菜單中的所有名字:
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name)
這行代碼讓Python從列表vegetables中取出一個名字,并將其存儲在變量name中。最后,我們讓Python打印前面存儲的變量name中的名字。這樣,對于列表中的每個名字,Python都將重復執(zhí)行print(name)代碼。你可以這樣解讀這些代碼:對于列表vegetables中的每種蔬菜,都將其名字打印出來。輸出很簡單,就是列表中所有蔬菜的姓名:
potato
tomato
onion
詳解遍歷列表執(zhí)行過程
循環(huán)這種概念很重要,因為它是讓計算機自動完成重復工作的常見方式之一。例如,在前面的代碼中使用的簡單循環(huán)中,Python將首先讀取其中的第一行代碼:
for name in vegetables:
這行代碼讓Python獲取列表vegetables中的第一個值potato,并將其存儲到變量name 中。接下來,Python讀取下一行代碼:
print(name)
它讓Python打印vegetables的值potato。由于該列表還包含其他值,Python返回到循環(huán)的第一行:
for name in vegetables:
Python獲取列表中的下一個名字tomato,并將其存儲到變量name中,再執(zhí)行下面這行代碼:
print(name)
Python再次打印變量vegetables的值tomato。
接下來,Python再次執(zhí)行整個循環(huán),對列表中的最后一個值onion進行處理。
至此,列表中沒有其他的值了,因此Python接著執(zhí)行程序的下一行代碼。在這個示例中,for循環(huán)后面沒有其他的代碼,因此程序就此結束。
剛開始使用循環(huán)時需要牢記,對列表中的每個元素,都將執(zhí)行循環(huán)指定的步驟,而不管列表包含多少個元素。如果列表包含一百萬個元素,Python就重復執(zhí)行指定的步驟一百萬次,且通常速度非???。
另外,編寫for循環(huán)時,對于用于存儲列表中每個值的臨時變量,可指定任何名稱。比如說:
for dog in dogs:
for cat in cats:
for item in item_list:
這些寫法都是可以的。
在For循環(huán)中做更多操作
在for循環(huán)中,可以獲取到每一個元素,可對每個元素執(zhí)行任何操作。比如說我們對每一種蔬菜都輸出一句話。
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name + ' is good !')
相比于前一個示例,唯一的不同是對于每種蔬菜,都打印了一條以其名字為抬頭的消息。這個循環(huán)第一次迭代時,變量name的值為potato,因此Python打印的第一條消息的抬頭為potato。第二次迭代時,消息的抬頭為tomato,而第三次迭代時,抬頭為onion。
下面的輸出表明,對于列表中的每種蔬菜,都打印了一條個性化消息:
potato is good !
tomato is good !
onion is good !
在for循環(huán)中,想包含多少行代碼都可以。在代碼行for name in vegetables后面,每個縮進的代碼行都是循環(huán)的一部分,且將針對列表中的每個值都執(zhí)行一次。因此,可對列表中的每個值執(zhí)行任意次數(shù)的操作。
下面再添加一行代碼:
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name + ' is good !')
print(name + ' is a vegetable!')
由于兩條print語句都縮進了,因此它們都將針對列表中的每位蔬菜都執(zhí)行一次。輸出結果如下:
potato is good !
potato is a vegetable!
tomato is good !
tomato is a vegetable!
onion is good !
onion is a vegetable!
在for循環(huán)中,想包含多少行代碼都可以。這種方式在開發(fā)過程中很有用。
避免縮進錯誤
Python根據(jù)縮進來判斷代碼行與前一個代碼行的關系。在前面的示例中,對每種蔬菜的輸出代碼行是for循環(huán)的一部分,因為它們縮進了。Python通過使用縮進讓代碼更易讀。
簡單地說,它要求你使用縮進讓代碼整潔而結構清晰。在較長的Python程序中,你將看到縮進程度各不相同的代碼塊,這讓你對程序的組織結構有大致的認識。 當你開始使用縮進時,需要注意一些常見的縮進錯誤。
例如,有時候,程序員會將不需要縮進的代碼塊縮進,而對于必須縮進的代碼塊卻忘了縮進。通過查看這些錯誤示例,有助于我們以后避開它們,以及在它們出現(xiàn)在程序中時進行修復。下面來看一些較為常見的縮進錯誤。
忘記縮進
對于位于for語句后面且屬于循環(huán)組成部分的代碼行,一定要縮進。如果你忘記縮進,運行會直接報錯:
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name)
print語句應縮進卻沒有縮進。Python沒有找到期望縮進的代碼塊時,會讓你知道哪行代碼有問題。
File "<stdin>", line 2
print(name)
^
IndentationError: expected an indented block
通常,將緊跟在for語句后面的代碼行縮進,可消除這種縮進錯誤。
忘記縮進額外的代碼行
有時候,循環(huán)能夠運行而不會報告錯誤,但結果可能會出乎意料。試圖在循環(huán)中執(zhí)行多項任務,卻忘記縮進其中的一些代碼行時,就會出現(xiàn)這種情況。
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name + ' is good !')
print(name + ' is a vegetable!')
第二個print語句原本需要縮進,但Python發(fā)現(xiàn)for語句后面有一行代碼是縮進的,因此它沒有報告錯誤。最終的結果是,對于列表中的每種蔬菜,都執(zhí)行了第一條print語句,因為它縮進了;而第二條print語句沒有縮進,因此它只在循環(huán)結束后執(zhí)行一次。由于變量 name 的終值為onion,因此只有一條輸出了onion is a vegetable!:
potato is good !
tomato is good !
onion is good !
onion is a vegetable!
這是一個邏輯錯誤。從語法上看,這些代碼是沒問題的,但由于存在邏輯錯誤,結果并不符合預期。如果你預期某項操作將針對每個列表元素都執(zhí)行一次,但它卻只執(zhí)行了一次,請確定是否需要將一行或多行代碼縮進。
不必要的縮進
如果你不小心縮進了無需縮進的代碼行,同樣運行的時候也會報錯:
message = "Hello Python world!"
print(message)
print語句無需縮進,因為它并不屬于前一行代碼,運行的時候會幫我們指出這種錯誤:
print(message)
^
IndentationError: unexpected indent
為避免意外縮進錯誤,請只縮進需要縮進的代碼。在前面編寫的程序中,只有要在for循環(huán)中對每個元素執(zhí)行的代碼就需要縮進。
循環(huán)后不必要的縮進
如果我們不小心縮進了應在循環(huán)結束后執(zhí)行的代碼,這些代碼將針對每個列表元素重復執(zhí)行。 在有些情況下,這可能導致Python報告語法錯誤,但在大多數(shù)情況下,這只會導致邏輯錯誤。例如:
vegetables = ['potato','tomato','onion']
for name in vegetables:
print(name + ' is good !')
print(name + ' is a vegetable!')
## 這一行代碼被縮進
print('There are three kinds of vegetables.')
那么輸出就會變成以下這個樣子:
potato is good !
potato is a vegetable!
There are three kinds of vegetables
tomato is good !
tomato is a vegetable!
There are three kinds of vegetables
onion is good !
onion is a vegetable!
There are three kinds of vegetables.
這也是一個邏輯錯誤。Python不知道你的本意,只要代碼符合語法,它就會運行。所以我們應該時刻保持警惕,不要用錯了縮進。
遺漏了冒號
for語句末尾的冒號告訴Python,下一行是循環(huán)的第一行。
vegetables = ['potato','tomato','onion']
for name in vegetables
print(name + ' is good !')
for name in vegetables
^
SyntaxError: invalid syntax
如果你不小心遺漏了冒號,如上所示,將導致語法錯誤,因為Python不知道你意欲何為。這種錯誤雖然易于消除,但并不那么容易發(fā)現(xiàn)。
數(shù)值列表
Python函數(shù)range()讓你能夠輕松地生成一系列的數(shù)字。例如,可以像下面這樣使用函數(shù)range()來打印一系列的數(shù)字:
for value in range(1,5):
print(value)
上述代碼好像應該打印數(shù)字1~5,但實際上它不會打印數(shù)字5:
1
2
3
4
在這個示例中,range()只是打印數(shù)字1~4,這是你在編程語言中經(jīng)??吹降牟钜恍袨榈慕Y果。函數(shù)range()讓Python從你指定的第一個值開始數(shù),并在到達你指定的第二個值后停止,因此輸出不包含第二個值(這里為5)。
要打印數(shù)字1~5,需要使用range(1,6):
for value in range(1,6):
print(value)
這樣,輸出將從1開始,到5結束:
1
2
3
4
5
使用range()時,如果輸出不符合預期,請嘗試將指定的值加1或減1。
使用range()創(chuàng)建數(shù)字列表
要創(chuàng)建數(shù)字列表,可使用函數(shù)list()將range()的結果直接轉(zhuǎn)換為列表。如果將range()作為list()的參數(shù),輸出將為一個數(shù)字列表。
在上面的示例中,我們打印了一系列數(shù)字。要將這些數(shù)字轉(zhuǎn)換為一個列表,可使用list():
numbers = list(range(1,6))
print(numbers)
結果如下:
[1, 2, 3, 4, 5]
使用函數(shù)range()時,還可指定步長。例如,下面的代碼打印1~10內(nèi)的偶數(shù):
even_numbers = list(range(2,11,2))
print(even_numbers)
在這個示例中,函數(shù)range()從2開始數(shù),然后不斷地加2,直到達到或超過終值(11),因此 輸出如下:
[2, 4, 6, 8, 10]
使用函數(shù)range()幾乎能夠創(chuàng)建任何需要的數(shù)字集,例如,如何創(chuàng)建一個列表,其中包含前10個整數(shù)(即1~10)的平方呢?在Python中,兩個星號**表示乘方運算。下面的代碼演示如何將前10個整數(shù)的平方加入到一個列表中:
squares = []
for value in range(1,11):
square = value**2
squares.append(square)
print(squares)
首先,我們創(chuàng)建了一個空列表;接下來,使用函數(shù)range()讓Python遍歷1~10的值。在循環(huán)中,計算當前值的平方,并將結果存儲到變量square中。然后,將新計算得到的平方值附加到列表squares末尾。最后,循環(huán)結束后,打印列表squares:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
對數(shù)字列表執(zhí)行簡單的統(tǒng)計計算
有幾個專門用于處理數(shù)字列表的Python函數(shù)。例如,你可以輕松地找出數(shù)字列表的最大值、最小值和總和:
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
### 輸出最小值
print(min(digits))
### 輸出最大值
print(max(digits))
### 計算總和
print(sum(digits))
0
9
45
列表解析
列表解析將for循環(huán)和創(chuàng)建新元素的代碼合并成一行,并自動附加新元素。下面的示例使用列表解析創(chuàng)建你在前面看到的平方數(shù)列表:
squares = [value**2 for value in range(1,11)]
print(squares)
要使用這種語法,首先指定一個描述性的列表名,如squares;然后,指定一個左方括號,并定義一個表達式,用于生成你要存儲到列表中的值。在這個示例中,表達式為value**2,它來計算平方值。接下來,編寫一個for循環(huán),用于給表達式提供值,再加上右方括號。在這個示例中,for循環(huán)為for value in range(1,11),它將值1~10提供給表達式value**2。請注意,這里的for語句末尾沒有冒號。
結果與你在前面看到的平方數(shù)列表相同:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
要創(chuàng)建自己的列表解析,需要經(jīng)過一定的練習,但能夠熟練地創(chuàng)建常規(guī)列表后,你會發(fā)現(xiàn)這樣做是完全值得的。當你覺得編寫三四行代碼來生成列表有點繁復時,就應考慮創(chuàng)建列表解析了。
列表中的一部分
在上面的內(nèi)容中,我們學習了如何訪問單個列表元素。接下來,我們將學習如何處理列表的所有元素。我們還可以處理列表的部分元素——Python稱之為切片。
切片
要創(chuàng)建切片,可指定要使用的第一個元素和最后一個元素的索引。與函數(shù)range()一樣,Python 在到達你指定的第二個索引前面的元素后停止。要輸出列表中的前三個元素,需要指定索引0~3, 這將輸出分別為0、1和2的元素。
我們還是以蔬菜列表為例:
vegetables = ['potato','tomato','onion','leek']
print(vegetables[0:3])
上面的代碼打印該列表的一個切片,其中只包含三種蔬菜。輸出也是一個列表,其中包含前三種蔬菜:
['potato', 'tomato', 'onion']
你可以生成列表的任何子集,例如,如果你要提取列表的第2~4個元素,可將起始索引指定為1,并將終止索引指定為4:
vegetables = ['potato','tomato','onion','leek']
print(vegetables[1:4])
這一次,切片始于tomato,終于leek:
['tomato', 'onion', 'leek']
如果你沒有指定第一個索引,Python將自動從列表開頭開始:
vegetables = ['potato','tomato','onion','leek']
print(vegetables[:4])
由于沒有指定起始索引,Python從列表開頭開始提取:
['potato', 'tomato', 'onion', 'leek']
要讓切片終止于列表末尾,也可使用類似的語法。例如,如果要提取從第3個元素到列表末 尾的所有元素,可將起始索引指定為2,并省略終止索引:
vegetables = ['potato','tomato','onion','leek']
print(vegetables[2:])
Python將返回從第3個元素到列表末尾的所有元素:
['onion', 'leek']
無論列表多長,這種語法都能夠讓你輸出從特定位置到列表末尾的所有元素。前面我們了解過,負數(shù)索引返回離列表末尾相應距離的元素,因此你可以輸出列表末尾的任何切片。例如,如果你要輸出名單上的最后三種蔬菜,可使用切片vegetables[-3:]:
vegetables = ['potato','tomato','onion','leek']
print(vegetables[-3:])
['tomato', 'onion', 'leek']
遍歷切片
如果要遍歷列表的部分元素,可在for循環(huán)中使用切片。在下面的示例中,我們遍歷前三種蔬菜,并打印它們的名字:
vegetables = ['potato','tomato','onion','leek']
print("Here are the first three vegetable:")
for name in vegetables[:3]:
print(name.title())
輸出結果:
Here are the first three vegetable:
Potato
Tomato
Onion
在很多情況下,切片都很有用。例如,編寫游戲時,你可以在玩家退出游戲時將其最終得分加入到一個列表中。然后,為獲取該玩家的三個最高得分,你可以將該列表按降序排列,再創(chuàng)建一個只包含前三個得分的切片。處理數(shù)據(jù)時,可使用切片來進行批量處理;編寫Web應用程序時,可使用切片來分頁顯示信息,并在每頁顯示數(shù)量合適的信息。
復制列表
我們經(jīng)常需要根據(jù)既有列表創(chuàng)建全新的列表。下面來介紹復制列表的工作原理,以及復制列表可提供極大幫助。
要復制列表,可創(chuàng)建一個包含整個列表的切片,方法是同時省略起始索引和終止索引([:])。 這讓Python創(chuàng)建一個始于第一個元素,終止于最后一個元素的切片,即復制整個列表。
例如,假設有一個列表,其中包含你最喜歡的四種食品,而你還想創(chuàng)建另一個列表,在其中包含一位朋友喜歡的所有食品。不過,你喜歡的食品,這位朋友都喜歡,因此你可以通過復制來創(chuàng)建這個列表:
my_foods = ['pizza', 'falafel', 'carrot cake'] friend_foods = my_foods[:]
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
我們首先創(chuàng)建了一個名為my_foods的食品列表,然后創(chuàng)建了一個名為friend_foods的新列表。我們在不指定任何索引的情況下從列表my_foods中提取一個切片,從而創(chuàng)建了這個列表的副本,再將該副本存儲到變量friend_foods中。打印每個列表后,我們發(fā)現(xiàn)它們包含的食品相同:
My favorite foods are:
['pizza', 'falafel', 'carrot cake']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake']
為了核實我們確實有兩個列表,下面在每個列表中都添加一種食品,并核實每個列表都記錄了相應人員喜歡的食品:
my_foods = ['pizza', 'falafel', 'carrot cake'] 5
friend_foods = my_foods[:]
my_foods.append('cannoli')
friend_foods.append('ice cream')
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
與前一個示例一樣,我們首先將my_foods的元素復制到新列表friend_foods中。接下來,在每個列表中都添加一種食品:在列表my_foods中添加cannoli,而在friend_foods中添加ice cream。最后,打印這兩個列表,核實這兩種食品包含在正確的列表中。
My favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake', 'ice cream']
上面的輸出表明,cannoli包含在你喜歡的食品列表中,而ice cream沒有。ice cream包含在你朋友喜歡的食品列表中,而cannoli沒有。倘若我們只是簡單地將my_foods賦給friend_foods,就不能得到兩個列表。例如,下例演示了在不使用切片的情況下復制列表的情況:
my_foods = ['pizza', 'falafel', 'carrot cake']
#這行不通
friend_foods = my_foods
my_foods.append('cannoli')
friend_foods.append('ice cream')
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
這里將my_foods賦給friend_foods,而不是將my_foods的副本存儲到friend_foods。這種語法實際上是讓Python將新變量friend_foods關聯(lián)到包含在my_foods中的列表,因此這兩個變量都指向同一個列表。鑒于此,當我們將cannoli添加到my_foods中時,它也將出現(xiàn)在friend_food中;同樣,雖然ice cream好像只被加入到了friend_foods中,但它也將出現(xiàn)在這兩個列表中。
輸出表明,兩個列表是相同的,這并非我們想要的結果:
My favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
8-1 動物:想出至少三種有共同特征的動物,將這些動物的名稱存儲在一個列表中,再使用for循環(huán)將每種動物的名稱都打印出來。
修改這個程序,使其針對每種動物都打印一個句子,如"A dog would make a great pet"。在程序末尾添加一行代碼,指出這些動物的共同之處,如打印諸如"Any of these animals would make a great pet!"這樣的句子。
8-2 數(shù)到 20:使用一個 for 循環(huán)打印數(shù)字 1~20(含)。
8-3 計算 1~ 1000000 的總和:創(chuàng)建一個列表,其中包含數(shù)字1~1000000,再使用min()和max()核實該列表確實是從1開始,到1000000 結束的。另外,對這個列表調(diào)用函數(shù)sum(),體會一下Python將一百萬個數(shù)字相加需要多長時間。
8-4 3的倍數(shù):創(chuàng)建一個列表,其中包含 3~30 內(nèi)能被3整除的數(shù)字;再使用一個for循環(huán)將這個列表中的數(shù)字都打印出來。
8-5 切片:修改8-1的代碼,在末尾添加幾行代碼,以完成如下任務。 打印消息"The first three items in the list are:",再使用切片來打印列表的前三個元素。打印消息"Three items from the middle of the list are:",再使用切片來打印列表中間的三個元素。打印消息"The last three items in the list are:",再使用切片來打印列表末尾的三個元素。
想查看作業(yè)答案可以去我的Githu倉庫