tuple
April 11, 2022tuple
許多地方都跟 list
很像,它們都是循序類型,只不過 tuple
不可變動。
tuple 操作
tuple
建立後就無法變動,想要建立 tuple
,只要在某個值後面加上一個逗號「,」就可以。例如:
>>> 10,
(10,)
>>> 10, 20, 30,
(10, 20, 30)
>>> acct = 1, 'Justin', True
>>> acct
(1, 'Justin', True)
>>> type(acct)
<class 'tuple'>
>>>
建立 tuple
時,最後一個逗號可以省略,雖然只要在值之後加上逗號就可以了,不過,通常會加上 ()
讓人一眼就看出這是個 tuple
,例如 (1, 2, 3)
、(1, 'Justin', True)
,不過要注意,只包含一個元素的 tuple
,不能寫成 (elem)
,而是要寫成 elem,
或者是 (elem,)
,如果要建立沒有任何元素的 tuple
,倒是可以只寫 ()
。
不可變動的循序類型,都具有以下的行為:
x in s
:s
是否包含x元素。x not in s
:s
是否未包含x元素。s + t
:串接s
與t
。s * n
:將s
的元素重複n
次。s[i]
:取得索引i
處的元素,第一個索引是 0。s[i:j]
:切割出從i
到j
的元素。s[i:j:k]
:切割出從i
到j
的元素,每次間隔k
。len(s)
:取得s
的長度。mix(s)
:取得s
中的最小值。max(s)
:取得s
中的最大值。s.index(x[, i[, j]])
:取第一個x
的索引位置(可指定從i
開始,至j
之前)。s.count(x)
:取得x
的出現次數。
tuple 的作用
tuple
可以做什麼呢?有時想要傳回一組相關的值,又不想特地定義一個型態,就會使用 tuple
,像是 (1, 'Justin', True)
,也許就代表了從資料庫中臨時撈出來的一筆資料。
有時希望某函式不要修改傳入的資料,因為 tuple
無法變動,這時就可將資料放在 tuple
傳入,萬一函式的實作者試圖修改資料,執行時就會出錯,也就會知道有人試圖做出規格外的事情。另外,tuple
佔用的記憶體空間比較小。
簡單來說,tuple
可以作為簡便的資料載體(data carrier),因為不可變動,代表著有固定結構,包含資料的欄位順序、長度,而不可變動代表 tuple
記錄了唯一的狀態,沒有變動為其他狀態的可能性,也就是不會有隱藏的狀態。
什麼樣的資料,會需要記錄欄位順序、長度且不可變動呢?
最簡單易懂的例子就是座標點,像是:(10,)
代表一維座標,(20, 30)
代表著二維座標,若是笛卡兒座標系,20 代表 x 座標,30 代表 y 座標;若是極座標系,兩個欄位就分別代表半徑與角度;必要時,也可以使用更多的元素來表示更高維度,例如四元數。
也就是說,如果想將資料欄位順序、長度組成,視為一種資料集合(例如笛卡兒座標系的點集合),若不想大費周章地定義一個型態時,就可以使用 tuple
。
元素拆解
可以將 tuple
中的元素拆解(Unpack),逐一分配給每個變數,例如:
>>> data = (1, 'Justin', True)
>>> id, name, verified = data
>>> id
1
>>> name
'Justin'
>>> verified
True
>>>
記得嗎?tuple
的 ()
括號可以省略,雖然多數情況下,為了表示是個 tuple
而寫括號,然而以下情況省略括號,卻是 Python 最常被拿來津津樂道的特性之一:
>>> x = 10
>>> y = 20
>>> x, y = y, x
>>> x
20
>>> y
10
>>>
這個置換(Swap)變數值的動作,在其他語言中,通常需要一個暫存變數,而在 Python 只要一行就可以完成。
拆解元素指定給變數的特性,其實在 list
、set
等物件上,也可以使用,例如 x, y, z = [1, 2, 3]
的結果,x
會是 1、y
會是 2 而 z
會是 3。
也可以使用 *
來拆解可迭代物件,這特性稱為 Extended Iterable Unpacking,例如:
>>> a, *b = (1, 2, 3, 4, 5) # 拆解 tuple
>>> a
1
>>> b
[2, 3, 4, 5]
>>> a, *b, c = [1, 2, 3, 4, 5] # 拆解 list
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
>>> a, *b, c = range(5) # 拆解range
>>> a
0
>>> b
[1, 2, 3]
>>> c
4
>>> a, *b = {1, 2, 3} # 拆解 set
>>> a
1
>>> b
[2, 3]
>>> a, *b = {'x': 1, 'y': 2, 'z': 3} # 拆解 dict
>>> a # 取得的是鍵
'x'
>>> b
['y', 'z']
>>>
在某個變數上指定星號「*
」,其他變數被分配了單一個值之後,剩餘的元素,就會以 list
指定給標上了星號的變數。
Python 3.5 以後,增加了 Additional Unpacking Generalizations 特性,可以將可迭代物件拆解至 list
、set
、tuple
、dict
,以 set
為例:
>>> s1 = {'哈', '囉'}
>>> s2 = {'哈', '啦'}
>>> r = {*s1, *s2}
>>> r
{'啦', '囉', '哈'}
>>>
這成為合併 set
、list
、tuple
的另一種方式;雖然 *
也可以應用於 dict
,不過僅會拆解出鍵,若想同時拆解出鍵值,可以使用 **
,例如:
>>> d1 = {'a': 10, 'b': 20}
>>> d2 = {'b': 30, 'c': 40}
>>> r1 = {*d1, *d2}
>>> r2 = {**d1, **d2}
>>> r1
{'c', 'b', 'a'}
>>> r2
{'a': 10, 'b': 30, 'c': 40}
>>>
其實元素拆解(Unpack)這個特性,是 Python 3.9 以前對〈模式比對(pattern matching)〉的簡易支援;Python 3.10 開始有了 match-case
特性,則是更完善地支援了模式比對。
在 Python 3.5 以後,3.9 之前,**
的這個新特性,可用來簡單地合併多 dict
,Python 3.9 以後,如〈字典〉談過的,可以透過 |
來更簡單地合併 dict
。