串列
簡介
前面介紹到的資料型態,像是整數、浮點數、字串或是布林值,都是比較簡單的資料型態,除了這種比較單純的值之外,在 Python 有好幾種「集合」或「容器」型的資料結構,通常這些型式的資料結構都可以用來存放很多資料,在 Python 裡最常用的一種叫做「串列(List)」。串列是一塊連續的記憶體位置,你可以把這一堆連續的記憶體想像一個一個小格子,每個格子成有隔板隔開,有點像用來放藥的藥盒子,裡面可以放各式各樣的東西:
在介紹串列之前,大家有沒想過為什麼需要這種東西?一般的數字或字串不夠用嗎?在一般的場合是很夠用的,不過如果想要一口氣把一堆相同或類似的資料放在同一個變數裡,或是想把一堆資料傳給另一個函數進行處理的時候,這時候使用這種集合型的資料型態就會比較方便了。
在 Python 的串列寫起來像這樣:
heroes = ["悟空", "鳴人", "魯夫"]
score = [10, 30, 50]
串列的外層是一對中括號,裡面的東西通常會稱它為「元素(Element)」,每個元素之間使用逗號分開。看到這裡,如果各位曾經接觸過其他程式語言,大概會認為「這不就是『陣列(Array)』嗎?怎麼到了 Python 就改叫串列了?」
若以使用的手感來看,串列的確是很像陣列沒錯,你也可以把它當成陣列基本上不會出什麼亂子,說不定原本已經寫了一陣子的 Python 工程師朋友也不一定能講出這之間有什麼差別。它們雖然形狀看起來很像、用起來也很像,不過在本質上還是有些差異,並不是只有稱呼不同而已,在本章最後的冷知識會有比較詳細的補充說明。
陣列跟串列同樣都是一塊連續的記憶體位置,某些程式語言的陣列(例如 C 語言或 Rust),裡面存放的東西規定必須要是相同型別,要放字串就全部都放字串,要放數字就只能放數字,甚至整數跟浮點數還不能混著放,有的更嚴格的甚至是陣列一經宣告就不能再增加元素的也有。而在 Python 的串列沒有管這麼嚴格,串列裡的內容物你想怎麼放就怎麼放:
heroes = ["悟空", "鳴人", "魯夫"]
something = [1, True, None, 3.14, "python", heroes, [1, 2, 3]]
想放字串、數字、布林值都行,放個空值 None
也行,或是在串列裡面再放一層串列都沒問題,想放什麼就放什麼。
串列常見操作
有幾個元素?
先從最簡單的操作開始,先來試著印出串列裡有多 少顆元素,Python 有個內建函數 len()
可以做這件事:
>>> heroes = ["悟空", "鳴人", "魯夫", "流川楓", "芙莉蓮"]
>>> len(heroes)
5
雖然 len()
函數字面上的意思是「長度(length)」,但它算出來的結果就是這個陣列有幾顆元素。事實上,不只是串列,在 Python 只要是可以一個一個把東西拿出來的東西,都能使用 len()
函數取得個數,例如前面介紹過的字串,以及後面章節會介紹的字典、Tuple 跟集合也都能用。
索引值
接下來,如果想要取出串列裡的某一個元素,使用中括號搭配「索引值(Index)」就能取得指定元素。在 Python 的串列索引值是從 0 開始算,例如我想要印出「悟空」的話,它在這個 heroes
裡是第一顆元素,它的索引值就是 0。同理,在串列裡「魯夫」是第三顆元素,它的索引值就是 2。我們可以把它們印出來試試看:
>>> heroes = ["悟空", "鳴人", "魯夫", "流川楓", "芙莉蓮"]
>>> heroes[0]
'悟空'
>>> heroes[2]
'魯夫'
如果想要印出最 後一個「芙莉蓮」的話呢?因為它是最後一個,掐指一算就會知道它的索引值是 4,所以使用 heroes[4]
就行了。這樣寫是沒問題,但如果遇到串列比較長一點的,算起來就有點麻煩。在 Python 有更簡單的寫法,就是使用負的索引值,負的索引值可以從後面算回來,例如 -1
就會取得這個串列的最後一顆元素,同理可證,索引值 -2
就會取得「流川楓」:
>>> heroes[-1]
'芙莉蓮'
>>> heroes[-2]
'流川楓'
那如果使用超過範圍的索引值呢?像這樣:
print(heroes[1450]) # 會印出什麼?
很明顯這裡並沒有這麼多元素,所以執行之後就得到 IndexError
錯誤訊息,跟你抱怨你給的索引值超過範圍了:
IndexError: list index out of range
使用索引值不只可以從串列裡取得元素,也可以把指定位置的元素換掉:
>>> heroes = ["悟空", "鳴人", "魯夫", "流川楓", "芙莉蓮"]
>>> heroes[0] = "JoJo"
>>> heroes[-1] = "DIO"
>>> heroes
['JoJo', '鳴人', '魯夫', '流川楓', 'DIO']
上面這段程式碼意思,就是分別把第一個元素「悟空」換成「JoJo」,並且把最後一個元素「芙莉蓮」換成「DIO」。
巢狀串列
所謂的巢狀串列(Nested List)就是指串列裡又包了其他的串列,大概就是大腸包小腸的概念,看起來可能會像這樣:
heroes = [["悟空", "達爾"], ["鳴人", "佐助"], ["櫻木花道", "流川楓"]]
為什麼要在串列裡又包串列搞這麼複雜?通常是因為想把一些相關的資料放在一起,使用巢狀串列就會相對的更簡單、更有組織性一點。要取得巢狀串列裡的元素,也是透過索引值的方式,只是要再多一層 []
。例如我想印出在第 2 個元素裡的第 1 個元素「鳴人」:
>>> heroes[1][0]
'鳴人'
如果想要把整個巢狀串列裡的元素都印出來的話,用兩層迴圈就能做到:
heroes = [["悟空", "達爾"], ["鳴人", "佐助"], ["櫻木花道", "流川楓"]]
for hero in heroes:
for character in hero:
print(character)
這樣就搞定了。
《冷知識》為什麼索引值從 0 開始?
有沒有想過為什麼我們從小學習的數學是從 1 開始算,但好像大部份比較主流的程式語言的陣列包括 Python 的串列,索引值卻是從 0 開始算?因為這是因為陣列的索引值並不是指向元素本身,比較像是記憶體位置的「偏移量」。假設有一個陣列像這樣:
chars = ['a', 'b', 'c', 'd']
當我們說「變數 chars
指向 ['a', 'b', 'c', 'd']
」的時候,並不是 chars
這個變數直接指向這一整個陣列,而是指向這個陣列的「起始位置」,更精準的說是記憶體位置。陣列在記憶體裡的位置跟門牌號碼一樣都是連號的,假設這個陣列的起始記憶體位置是 1450
,然後每個元素所佔的位置或說格子的大小我先隨便假設是 8(先不管它的單位以及為什麼是 8)。大概示意圖如下:
這樣的話,你覺得要如何取 得第一個元素的 a
字元?不就是指向記憶體位置 1450
就拿到了嗎?如果知道怎麼拿第一個元素,要怎麼拿第二個、第三個元素?這簡單,因為每個格子的寬度是固定的,所以只要知道第一個元素的位置再搭配簡單的加法跟乘法就能算出來了:
第 2 個元素 = 1450 + (1 x 8) = 1458
第 3 個元素 = 1450 + (2 x 8) = 1466
以此類推,所以我們就能推導出第 N 個元素的計算公式:
第 N 個元素 = 起始位置 + ((N - 1) x 8)
到這裡,大家可能也有發現上面公式裡的 N - 1
,剛好就是我們平常在講的索引值。所以索引值其實就是記憶體位置的「偏移量」,因此才會從 0 開始。
陣列索引值從 0 開始算的概念是從滿早期的程式語言開始就有了,大家用著用著也習慣了,Python 以及其他程式語言也都受到影響,所以 Python 的串列索引值也跟著從 0 開始算。
但並不是所有程式語言的索引值都是從 0 開始算,不同程式語言對這件事有不同的觀點,像程式語言 Lua、Fortran、COBOL、Julia 跟 R 語言等都是從 1 開始算的,也許在它們的觀點來看,chars[1]
就是第一個字或第一個欄位,看起來更簡單、直覺。
元素是否存在?
要如何知道某個元素是不是有在這個串列裡呢?有幾種做法,比較簡單又直覺的寫法,就是使用 in
關鍵字:
>>> heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
>>> "悟空" in heroes
True
>>> "芙莉蓮" in heroes
False
不管這個元素是否存在串列中,in
關鍵字一定會給一個布林值的答案,不會無聲卡。這很常在進行邏輯判斷的時候用到,例如:
heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
if "悟空" in heroes:
print("悟空在家")
else:
print("哥哥不在家,今天不賣酒!")
有沒有發現這語法還滿直覺的?就算你不會寫程式,只要略懂英文就大概能猜的出意思,這也是 Python 這個程式語言的優點之一。
除了 in
關鍵字之外,還可以透過串列本身提供的 .index()
方法來取得某個元素在這個串列裡的索引值:
>>> heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
>>> heroes.index("悟空")
1
"悟空"
在第 2 個位置,所以會得到索引值 1。不過要注意如果試著問它某個不存在的元素的話,像這樣:
>>> heroes.index("芙莉蓮")
如果 "芙莉蓮"
不在 heroes
串列裡,在其他程式語言可能不會出錯而是得到 -1
或是 undefined
之類的答案,表示不在隊伍裡。但 Python 會給你一個 ValueError
的錯誤訊息,很明確的告訴你這裡沒有這個人:
ValueError: '芙莉蓮' is not in list
另外還有一個方法是使用串列的 .count()
方法,這個方法會回傳某個元素在串列裡出現的次數,如果完全沒有出現的話會得到 0:
>>> heroes = ["五條悟", "鳴人", "鳴人", "鳴人", "鳴人", "鳴人", "三井壽"]
>>> heroes.count("鳴人")
5
>>> heroes.count("魯夫")
0
在上面這幾個方法,雖然我們使用串列的 .index()
或 .count()
方法來得知某個元素是否存在串列裡,但如果只是要判斷有或沒有的話,使用 in
關鍵字得到布林值可能會更適合而且更不容易出錯,看看底下這個例子,想想看答案是什麼:
heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
if heroes.index("五條悟"):
print("找到了")
else:
print("沒找到")
乍看之下,你可能會以為會印出 找到了
,但答案卻剛好相反。這是因為 .index()
方法會得到索引值 0,在前面布林值章節曾經介紹過數字 0 在 Python 裡會被判定成 False
,所以反而會印出 沒找到
,這結果可能不是你想要的。
印出所有元素
如果想把串列裡的所有元素都印出來,可以使用上個章節學到的 for
迴圈:
heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
for hero in heroes:
print(hero)
這裡我會使用 hero
當做迴圈裡的變數名稱,而不是隨便用個 a
或 i
這種沒什麼相對比較沒有意義的名字。變數叫什麼名字對 Python 來說並沒有差別,也沒有強制規定,反正執行之後的結果都一樣,但我希望在迴圈裡的變數名稱可以看起來知道自己在做什麼,所以我遇到這種一整群的資料會使用複數型態,而個別的變數我則會使用單數型態,這樣程式碼的可讀性會好一些。如果用 while
迴圈也沒問題,只是得額外加個變數:
heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
i = 0
while i < len(heroes):
print(heroes[i])
i += 1
看起來還是 for
迴圈比較簡單一點。
新增元素
串列有一些不錯用的方法,可以讓原有的串列加入新的元素,例如 .append()
方法可以把新的元素加到串列的最後面:
>>> heroes = ["悟空", "鳴人", "芙莉蓮"]
>>> heroes.append("娜美")
>>> heroes
['悟空', '鳴人', '芙莉蓮', '娜美']
這樣就可以把 "娜美"
加到 heroes
串列的最後面了。.append()
方法一次只能新增一個元素,如果想一口氣加入多個元素,可以使用 .extend()
方法來幫串列進行「擴充」:
>>> heroes = ["悟空", "鳴人", "芙莉蓮"]
>>> heroes.extend(["娜美", "魯夫", "索隆"]) # 一次加入 3 個
>>> heroes
['悟空', '鳴人', '芙莉蓮', '娜美', '魯夫', '索隆']
如果是要把元素從串列前面加進來的話,則是使用 .insert()
方法搭配索引值就能新的元素安插到指定的位置上,例如:
>>> heroes = ["悟空", "鳴人", "芙莉蓮"]
>>> heroes.insert(1, "魯夫")
>>> heroes
['悟空', '魯夫', '鳴人', '芙莉蓮']
.insert(1, "魯夫")
的意思是指要在索引值 1,也就是這個串列第 2 格的位置,安插元素 "魯夫"
。如果有看懂 .insert()
的用法的話,把索引值設定成 0 就等同於在最前面加入新元素,而把索引值設定成 len(heroes)
就等於是在最後面加入新元素,例如:
>>> heroes = ["悟空", "鳴人", "芙莉蓮"]
>>> heroes.insert(0, "五條悟")
>>> heroes
['五條悟', '悟空', '鳴人', '芙莉蓮']
移除元素
如果你對某個元素不滿意,想把它刪掉有好幾種做法,首先可使用串列的 .pop()
方法並搭配索引值就能把指定的元素拿出來:
>>> heroes = ["五條悟", "悟空", "鳴人", "芙莉蓮", "三井壽"]
# 拿掉最後一個元素
>>> heroes.pop()
'三井壽'
>>> heroes
['五條悟', '悟空', '鳴人', '芙莉蓮']
# 拿掉第一個元素
>>> heroes.pop(0)
'五條悟'
>>> heroes
['悟空', '鳴人', '芙莉蓮']
# 拿掉第二個元素
>>> heroes.pop(1)
'鳴人'
>>> heroes
['悟空', '芙莉蓮']
.pop()
方法如果沒有給索引值的話,會把串最的最後一個元素拿出來,如果有給索引值的話,則會取出索引值所指的元素。但萬一串列裡已經沒東西的話:
heroes = []
heroes.pop() # 這裡會...?
已經沒有東西可以拿啦!這時候 Python 會給你一個 IndexError
錯誤訊息,並且告訴你這個串列已經是空的了:
IndexError: pop from empty list
要刪除串列裡的元素,另一個做法是使用 .remove()
方法:
>>> heroes = ["五條悟", "悟空", "悟空", "鳴人", "三井壽"]
>>> heroes.remove("鳴人")
>>> heroes
['五條悟', '悟空', '悟空', '三井壽']
.remove()
方法可以帶入某個元素,如果在這個串列裡有出現的話就會把它移掉。不過需要注意的是,如果這個串列裡有兩個同樣的元素的時候,.remove()
方法只會拿掉第一個,剩下的會留著;
>>> heroes = ["五條悟", "悟空", "悟空", "鳴人", "三井壽"]
>>> heroes.remove("悟空")
>>> heroes
['五條悟', '悟空', '鳴人', '三井壽']
如果想要把所有的 "悟空"
都移掉的話,可以用我們在上個章節學到的迴圈:
heroes = ["五條悟", "悟空", "悟空", "鳴人", "三井壽"]
while "悟空" in heroes:
heroes.remove("悟空")
print(heroes) # 印出 ['五條悟', '鳴人', '三井壽']
像這樣跑個 while
迴圈就行 了。另外也可以使用 Python 的串列特有的「串列推導式(List Comprehension)」來做到一樣的效果,而且寫起來更簡潔,但這個我們後面一點的章節再來介紹。
相對的,使用 .remove()
方法的時候,萬一在這個串列裡完全沒有符合的元素的話:
heroes = ["五條悟", "悟空", "鳴人", "三井壽"]
heroes.remove("櫻木花道") # 這會...?
"櫻木花道"
不存在原本的 heroes
串列裡,跟前面介紹到的 .index()
方法一樣,Python 會給你一個 ValueError
的錯誤訊息,明確的跟你說這裡沒有這傢伙:
ValueError: list.remove(x): x not in list
假設如果整個串列裡的元素全部 都不要了,可使用串列的 .clear()
方法:
>>> heroes = ["悟空", "鳴人", "芙莉蓮"]
>>> heroes.clear()
>>> heroes
[]
就能全部都清掉了。
排序、反轉、合併
串列裡的元素可以使用串列 .sort()
方法來進行排序,這個方法的名字也相當直覺:
>>> numbers = [9, 5, 2, 7, 4, 3, 8, 1]
>>> numbers.sort()
>>> numbers
[1, 2, 3, 4, 5, 7, 8, 9]
你可能覺得這樣的結果看起來很理所當然,但要注意串列的 .sort()
方法會直接改變原本的串列。我不確定這是不是你想要的結果,如果你只是想要得到排序的結果但並不想改原本的串列的話,應該使用 Python 的內建函數 sorted()
:
>>> numbers = [9, 5, 2, 7, 4, 3, 8, 1]
>>> sorted_numbers = sorted(numbers)
>>> sorted_numbers
[1, 2, 3, 4, 5, 7, 8, 9]
>>> numbers
[9, 5, 2, 7, 4, 3, 8, 1]
注意,是 sorted()
函數,不是串列本身自帶的 .sort()
方法喔。這個方法很好用,只要是可以迭代的物件,都可以交給 sorted()
函數進行排序。
不管是串列本身的 .sort()
方法,還是內建函數 sorted()
,你有想過是依照什麼規則來排序嗎?數字的話可能還容易猜,如果是字串呢?例如:
>>> heroes = ["Batman", "Superman", "Hulk", "Ironman", "Captain America"]
>>> sorted(heroes) # 會印出什麼?
如果是要幫字串做排予的話,Python 會依照字串的第一個字元來排序,如果第一個字都一樣的話,就比第二個、第三個字元,直到比出輸贏為止。以上面這個例子來說,B
開頭的 "Batman"
會排在最前面,而 H
開頭的 "Hulk"
會排最後面。但如果想要照其他規則來排呢?我們可以指定排序的方式,也就是指定 key
參數,例如我想按照名字的「字數」來排,字母越少的排越前面:
>>> heroes = ["Batman", "Superman", "Hulk", "Ironman", "Captain America"]
>>> sorted(heroes, key=len)
['Hulk', 'Batman', 'Ironman', 'Superman', 'Captain America']
這裡 key=len
的意思是指定排序的規則是用內建的 len()
函數來計算字串的長度,算出來的數字越小的排越前面,這樣一來 4 個字的 "Hulk"
會排在最前面,而 "Captain America"
的字數最多所以會排在最後面。不管是內建的 sorted()
函數或是串列自帶的 .sort()
方法都可以指定 key
參數來決定排序的方法。不只內建的函數可以,自己寫的函數也可以,例如我希望排序的方式是「名字裡面有 man
」的角色排前面:
def name_with_man(name):
if "man" in name.lower():
return 0
return 1
heroes = ["Batman", "Superman", "Hulk", "Ironman", "Captain America"]
print(sorted(heroes, key=name_with_man))
這裡我定義了一個叫做 name_with_man()
的函數,如果名字裡有 man
就得到數字 0
,否則就是數字 1
,這樣最後在排序的時候有 man
的角色算出來的都是 0,排序的時候就會排在前面了。如果函數看不懂在寫什麼沒關係,在後面的章節還有更詳細的介紹,這裡先有個印象就好。
排序不只正向排序,雖然待會我們就會介紹到 .reverse()
方法可以用來反轉串列,但如果能在排序的時候就直接反向排序的話不是更簡單嗎?sorted()
函數跟 .sort()
方法都有一個 reverse
參數,它的預設值是 False
,也就是預設就是正向排序,如果把這個參數設定成 True
的話就會變成反向排序:
>>> numbers = [9, 5, 2, 7, 4, 3, 8, 1]
>>> numbers.sort(reverse=True) # 反向排序
>>> numbers
[9, 8, 7, 5, 4, 3, 2, 1]
如果只是要反轉的話,使用串列自帶的 .reverse()
方法也能做到這件事:
>>> numbers = [9, 5, 2, 7]
>>> numbers.reverse() # 反轉!
>>> numbers
[7, 2, 5, 9]
同樣要注意的是,.reverse()
方法跟 .sort()
方法一樣,都會直接改變串列本身。
最後,如果我想把串列裡的每個元素組成一個字串,例如我想把 ["悟空", "達爾", "蜘蛛人", "蝙蝠俠"]
變成 悟空x達爾x蜘蛛人x蝙蝠俠
,可以怎麼做?先使用 for
迴圈來試看看:
heroes = ["悟空", "達爾", "蜘蛛人", "蝙蝠俠"]
all_heroes = ""
for hero in heroes:
all_heroes += hero + "x"
all_heroes = all_heroes[:-1] # 把最後的 `x` 去掉
print(all_heroes) # 印出 悟空x達爾x蜘蛛人x蝙蝠俠
這感覺有點呆,而且最後還得再手動把最後一個 x
去掉,有點麻煩。我們在數字與字串的章節曾經介紹過的 .join()
方法也能用在這個地方,它可以讓我們更方便的做這件事:
>>> heroes = ["悟空", "達爾", "蜘蛛人", "蝙蝠俠"]
>>> "x".join(heroes)
'悟空x達爾x蜘蛛人x蝙蝠俠'
看到這裡,不知道大家有沒有發現不只是 .join()
方法可以用在不同的資料型態上,很多 Python 的方法都能用在不同但有類似設計的資料型態上,例如只要是能一個一個被拿出來的可迭代(Iterable)的物件都可以這樣玩。
可迭代物件
「迭代(Iteration)」的意思是指一個一個讀取物件裡面的元素的行為,如果是可以被這樣操作的物件,就可稱它為「可迭代」(Iterable)物件。在 Python 的世界中,前面曾經介紹過的字串,本章介紹的串列、待會就會提到的陣列,以及在後面章節會講到的元組(Tuple)、字典以及集合等等都是可迭代物件。不只這些內建的資料型態,就算是自己寫的物件只要符合某些規格就可以被視為可迭代物件,這就等後面介紹到「物件導向程式設計(OOP)」的時候再來說明。
Python 有內建一些專門用來處理可迭代物件的函數,而且還挺方便的,例如計算串列的總和 sum()
函數:
# 一般的數字串列
>>> sum([1, 4, 5, 0])
10
# 整數、浮點數混合有布林值的串列
>>> sum([2, True, 3.1, False])
6.1
只要串列裡元素都是數字或是布林值(布林值在 Python 裡面就是 0 跟 1),它就可以幫你做加總。但如果裡面有混了不是數字類型的元素,就會出現錯誤:
>>> sum(["1", 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
還有不久前才看到的 sorted()
函數可以用來排序:
# 對數字串列排序
>>> sorted([1, 4, 5, 0])
[0, 1, 4, 5]
# 對字串串列排序
>>> sorted(['z', 'y', 'x'])
['x', 'y', 'z']
# 字串也能排序
>>> sorted('hello')
['e', 'h', 'l', 'l', 'o']
另外,還有可以判斷可迭代物件裡面有沒有任何一個元素是 True
的函數 any()
,以及判斷是不是都是 True
的函數 all()
:
>>> any([False, False, False])
False
# 只要有一個是 True 就行了
>>> any([True, False, True])
True
# 所有的元素都要成立
>>> all([True, True, True])
True
# 只要有一個不成立就不行!
>>> all([True, False, True])
False
在上個章節介紹過的 enumerate()
函數也是用來處理可迭代物件,它會把可迭代物件裡的元素一個一個拿出來,加上索引值 再包成 Tuple 串列:
# 可以對串列這樣做
>>> heroes = ["悟空", "達爾", "蜘蛛人", "蝙蝠俠"]
>>> list(enumerate(heroes))
[(0, '悟空'), (1, '達爾'), (2, '蜘蛛人'), (3, '蝙蝠俠')]
# 字串也可以
>>> message = "hello"
>>> list(enumerate(message))
[(0, 'h'), (1, 'e'), (2, 'l'), (3, 'l'), (4, 'o')]
大家如果有機會查看 Python 官方文件的話,會發現很多可以用來處理可迭代物件的函數,只要參數是可迭代物件,不管是字串、串列、Tuple、字典、集合,甚至是自己寫的物件都可以丟進去玩看看。
同一個串列?
先來看看以下這個寫法:
>>> a = [1, 2, 3]
>>> b = a
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
這程式碼看起來應該還滿直覺的,看起來像是先建立串列 [1, 2, 3]
指定給變數 a
,然後讓變數 b
也指定成變數 a
,所以這時候變數 a
跟 b
都是串列 [1, 2, 3]
。嗯...這樣的講法我不能說你錯,只是不夠精準。
前面曾經有提過,在很多程式語言裡,一個等號不是「等於」的意思,而是「指定」的意思。指定不就是等於嗎?不完全是。當我們說 a = [1, 2, 3]
的時候,我們是在說變數 a
指向串列 [1, 2, 3]
,而不是說變數 a
等於串列 [1, 2, 3]
。同理,b = a
的意思是變數 b
指向變數 a
所指向的地方,也就是指向串列 [1, 2, 3]
。所以,你可以把 b = a
的結果看成以下這張圖:
你可能會好奇,就算是這樣又有什麼問題嗎?因為現在 a
跟 b
都指向同一個串列,所以如果我們透過其中任何一個變數修改串列的時候,另一個變數的值也會跟著變:
>>> a = [1, 2, 3]
>>> b = a
# 這時候 a 跟 b 是指向同一個串列
>>> a is b
True
# 試著改變串列 a 的第一個元素
>>> a[0] = 100
# 串列 a 的值改變了
>>> a
[100, 2, 3]
# 但串列 b 的值也跟著改變了
>>> b
[100, 2, 3]