使用者:Xyy23330121/Python/列表和元組


Python 中還提供了兩種好用的序列:列表和元組。其中列表是可變序列,而元組和字符串一樣是不可變序列。

列表和元組:基本方法

編輯

創建列表

編輯

用方括號括起來,不被解釋為索引的的內容會被視為列表,列表是由一系列變量組成的,每個變量之間由逗號,分隔。

a = [0, 1, 'string'] #创建一个列表,列表中可以包含任意的不同变量。
print(a)             #输出:[0, 1, 'string']
b = []               #可以创建空列表。

需要特別強調,列表中的元素也可以是一個列表。

c = [[0, 1], 2]

創建元組

編輯

用括號括起來,由多個用逗號隔開的值組成的是元組。

a = (0, 1, 'string')            #创建一个元组,元组中可以包含任意的不同变量。
print(a)                        #输出:(0, 1, 'string')
b = ()                          #可以创建空元组

需要特別強調,元組中的元素也可以是一個元組或列表。

c = ((0, 1), 2)
解釋元組的 Python 文檔(選讀)
Python 文檔中並沒有這樣介紹元組。Python 文檔中,對於有二個及以上元素的元組而言,沒有括號的元組輸入才是常態,而括號只是元組防止歧義的工具。只有對於空元組和一個元素的元組,才必須要加括號。


理解 Python 文檔的一個簡單方法是:把逗號當成優先級比「賦值」高的運算符。比如:1, 2, 3運算的結果是返回一個元素為123的元組。

然後,括號可以截斷這樣的運算符。

為了和一般的表達式區分開,創建只有一個元素的元組時,需要在元素後加一個括號,比如:

d = (0,)

為了方便起見,有2個或多個元素的元組,在不引發歧義的情況下,可以不加括號。

e = 0, 1
f = (0, 1), 2  #此时,作为 f 第一个元素的元组,必须要加括号。

前面賦值方法章節提到的序列解包,就是對元組進行的操作。

list函數 / tuple函數

編輯

類似 intfloat 等函數的行為,list 函數 / tuple 函數可以用於創建列表/元組。

創建空列表/空元組

編輯
a = list()
b = tuple()

不傳入任何參數的情況下,生成的是空列表/空元組。

將可迭代對象轉換為列表/元組

編輯
可迭代對象
在 Python 中,有一些對象被稱作是是「可迭代對象」。這類對象一般包含多個元素,而且有一個明顯的迭代順序。列表、元組和字符串就是可迭代對象。
可以簡單認為,既有多個元素,又有順序的對象都是可迭代對象。
a = list((1, 2, 3))
b = tuple([1, 2, 3])

任何可迭代對象都可以作為 list 函數 / tuple 函數的參數。

通用序列操作

編輯

大多數序列類型,包括可變序列和不可變序列都支持下面的操作。列表和元組就支持下面的操作。

加法與整數乘法

編輯
提示
拼接不可變序列時,每做一次加法都會生成一個新對象。比如:
a = (1,)
b = (2,)
c = (3,)
a+b+c

Python 會先計算a+b,在內存中存入(1, 2);然後再計算(1, 2)+c,在內存中存入(1, 2, 3)。在計算時存儲的總內容為: abc(1, 2)(1, 2, 3),即 8 個元組中元素的內存。通過模擬可以得出,運算時內存的占用,和元素所需總內存的平方是同階的。

為此,如果要連續拼接此類對象,應當使用 Python 為這些對象提供的方法,或者轉化為可變序列。比如:

  • 拼接字符串時,應當先構建由字符串組成的列表,再用str.join方法。
  • 拼接元組時,應當先將元組轉化為列表,再用+=運算符擴展列表。
  • 拼接bytes對象時,可以用bytes.join方法,也可以轉化為bytearray再用+=進行原地拼接。
關於用+=運算符擴展列表,參見下面的內容。關於 bytes 和 bytearray,參見「字符串和編碼」章節。

類似字符串,兩個列表相加、或兩個元組相加,返回的是拼接之後的結果;而列表或元組和整數n作乘法,返回的是重複 n 次後的結果。

print([1,2] + [3])  #加法:返回拼接之后的结果。
print((1,2) + (3,))
print([1,2] * 3)    #整数乘法:返回原列表/元组重复 n 次的结果。
print((1,2) * 3)

長度、索引與元素

編輯

除此之外,還有許多操作和字符串的結果類似。

a,b = [1, 2, 3], (1, 2, 3)
print(len(a),len(b))            #可以通过 len() 得到列表/元组的长度。
print(a[2],b[2])                #列表/元组支持索引。索引的方式和字符串类似。
print(a[::2],b[::2])

但是,和字符串返回對應位置的單個字符組成的子字符串不同,在使用整數作索引時,列表或元組會返回對應位置的元素,而非返回子列表或子元組。只有使用 slice 作索引時,總會返回一個子列表或子元組。

in / not in

編輯
print(1 in a, 1 not in b) #可以用比较表达式 in / not in 判断元素是否在列表/元组中。

和字符串不同,innot in 只判斷元素是否是列表/元組的元素,不會判斷子列表/子元組是否在原列表/原元組內。比如:

print([0,1] in [0,1,2]) #输出:False
print((0,1) in (0,1,2)) #输出:False
print("01" in "012")    #输出:True

最大值 / 最小值

編輯
a = (0, 1, 2)
b = [0, 1, 2]
print(max(a), min(b))  #输出:2 0

max 函數和 min 函數可以對序列中的元素進行比較。但這要求序列中所有元素之間兩兩可以比較。

查找元素 / 計數

編輯
a = (0, 1, 2, 2, 3, 2)
b = [0, 1, 2, 2, 3, 2]
print(a.index(2, 3, 5), b.count(2))  #输出:3 3

s.index(value[, start[, stop]]) 的運行結果等同於 s[start:stop].index(value),會輸出範圍內第一個等於 value 的元素的索引。

s.count(value) 和字符串不同,不支持 startstop 兩個參數。它會輸出整個列表中,該元素的個數。

用推導式創建列表/元組

編輯

除上面的操作之外,一個常用的操作是用推導式創建列表或元組。以下是一個簡單示例:

iterable = "Python"
s = [x for x in iterable]
t = (x for x in iterable)

此時,print(s,t)輸出的結果是:

['P', 'y', 't', 'h', 'o', 'n'] ('P', 'y', 't', 'h', 'o', 'n')

對於 a for b in c ,Python 會把可迭代對象 c 中的第一個迭代元素賦值到 b,然後把賦值後、表達式 a 的結果插入到列表或元組中。再把 c 中的第二個迭代元素賦值到 b,然後把賦值後、表達式 a 的結果插入到列表或元組中。直到可迭代對象沒有下一個迭代元素為止。

a for b in c 後面,還可以添加 if d。此時,在給 b 賦值後,只有表達式 d 的結果為 True ,才會把表達式 a 的結果插入到列表或元組中。比如以下形式:

iterable = "Python"
s = [x for x in iterable if x in "Thoughts"]
print(s)  #输出:['t', 'h', 'o']

列表的一些方法

編輯
可變序列操作表
運算 解釋 示例
s[i] = x 給可變序列中,索引為 i 的元素賦值。
如果索引為 i 的元素不存在,會報錯:
IndexError: list assignment index
out of range
s = [0, 1]
s[1] = 2

print(s)
輸出
[0, 2]
s[i:j] = t 按給出的子序列替換可變序列的元素。
s = [0, 1, 2]
s[1:] = [2, 3, 4]

print(s)
輸出
[0, 2, 3, 4]
del s[i:j] 刪除序列中,在該子序列中的元素。
等同於s[i: j] = []
s = [0, 1, 2]
del s[1:]

print(s)
輸出
[0]
s[i:j:k] = t 替換可變序列的子序列。
此時t的長度必須與子序列長度相匹配。
如果不匹配,會報錯 ValueError
s = [0, 1, 2]
s[::2] = [3, 4]

print(s)
輸出
[3, 1, 2]
del s[i:j:k] 刪除序列中,在該子序列中的元素。
s = [0, 1, 2]
del s[::2]

print(s)
輸出
[1]
s.append(x) 將元素 x 添加到序列的末尾。
等同於 s[len(s):len(s)] = [x]
s = [0, 1, 2]
s.append(3)

print(s)
輸出
[0, 1, 2, 3]
s.clear() s 中移除所有項
等同於 del s[:]
s = [0, 1]
s.clear()

print(s)
輸出
[]
s.copy() 創建並返回 s 的副本。
等同於 s[:]
該方法的作用將在之後講解。
s = [0, 1]
s1 = s.copy()

print(s1)
輸出
[0, 1]
s.extend(t) 用可迭代對象 t 中的元素擴展 s
s = [0]
s.extend((1,2))

print(s)
輸出
[0, 1, 2]
s += t s.extend(t)
這裡不能簡單理解為 s = s + t
s = [0]
s +=
"Py"
print(s)
輸出
[0, 'P', 'y']
s *= n 結果等同於 s = s * n
s = [0]
s *= 3

print(s)
輸出
[0, 0, 0]
s.insert(i, x) 在索引i處插入元素。使 s[i] == x
若索引 i < -len(s),則會插入在序列
開頭。若索引 i >= len(s),則會插入
在序列結尾。
s = [0, 1, 2]
s.insert(1, "1")

print(s)
輸出
[0, '1', 1, 2]
s.pop(i) 移除索引為 i 的元素,並返回該元素的
值。如果索引為 i 的元素不存在,則會
報錯:IndexError: pop index out of
range
s = [0, True, 'b']
a = s.pop(1)

print(a)
print(s)
輸出
True
[0, 'b']
s.pop() i 未給出,pop方法默認移除最後一
個元素。若序列為空序列,則會報錯:
IndexError: pop from empty list
s = [0, True, 'b']
a = s.pop()

print(a)
print(s)
輸出
'b'
[0, True]
s.remove(x) 刪除 s 中第一個等於 x 的元素。若沒
有符合標準的元素,則會報錯:
ValueError: list.remove(x): x not
in list
s = [0, True, 'b', True]
s.remove(True)

print(s)
輸出
[0, 'b', True]
s.reverse() 反轉列表。
s = [0, 1, 2]
s.reverse()

print(s)
輸出
[2, 1, 0]

列表:可變序列

編輯

列表是可變序列,元組是不可變序列。可變序列均支持此表格中的操作。

列表的排序

編輯

list.sort(*, key=None, reverse=False)

編輯

list.sort() 方法會對列表中的元素進行排序。我們有以下示例:

s = [0, 3, 1, 4]
s.sort()
print(s)   #输出:[0, 1, 3, 4]
s.sort(reverse = True)
print(s)   #输出:[4, 3, 1, 0]

關於參數key,我們將放在函數章節里學習。

注意:列表與賦值語句

編輯

在使用列表時,以下代碼可能不會得到想象中的結果:

a = [0, 1]
b = a
b[1] = 0
print(a)

這段代碼輸出的結果是[0, 0]!說明在更改 b 的時候,a 也被同步更改了!

以下代碼的輸出也有類似的問題:

a = [[]]*3
a[0].append(1)
print(a)       #输出[[1],[1],[1]]

如果讀者學習過 C 語言中關於指針的內容,可能很容易就能猜到原因。這裡不多講解原因,我們講講如何規避這樣的問題。

copy 方法

編輯

#列表的一些方法 章節中,有一個 s.copy() 方法。它可以用於解決上述問題的。我們將代碼改為:

a = [0, 1]
b = a.copy()
b[1] = 0
print(a)  #输出:[0, 1]

就解決了問題。但這對於嵌套的列表而言不起作用,比如:

a = [[0,1], [2,3]]
b = a.copy()
b[1][0] = 0
print(a)  #输出:[[0, 1], [0, 3]]

推導式方法

編輯

除 copy 方法外,我們還可以用推導式來複製列表。比如:

a = [0, 1]
b = [x for x in a]
b[1] = 0
print(a)  #输出:[0, 1]

我們可以嵌套多個推導式,來複製嵌套的列表。比如:

a = [[0,1], [2,3]]
b = [[xx for xx in x] for x in a]
b[1][0] = 0
print(a)  #输出:[[0, 1], [2, 3]]

就解決了問題。若已知列表是二維列表。也可以用:

a = [[0,1], [2,3]]
b = [x.copy() for x in a]
b[1][0] = 0
print(a)  #输出:[[0, 1], [2, 3]]

也可以解決問題。

任意維度嵌套列表的複製

編輯

使用推導式方法加一點控制,我們事實上可以做任意維度嵌套列表的複製。以下內容在學習函數章節之前可能較難理解。

def copy(seq): return [(copy(obj) if isinstance(obj, list) else obj) for obj in seq]

a = [0,0,[1,1,[2,2,[3,3]]]]
b = copy(a)
b[2][2][2][0] = 4
print(a)
print(b)