2017-04-26

Java 腦袋學 Python Tuple(唯讀的 List)

Tuple 對 Java 來說是一個陌生到不行的名字,甚至要翻成中文也是一個有看沒懂的狀態。

Google 翻譯管它叫做「元組」,這是中文,但是我不懂它在說什麼。

看看英英字典好了。
a structure of data that has several parts.
好像有那麼一點感覺了,我還是就叫它 Tuple 吧。

Python Tuple,唯讀的 List

可以看作是一種 immutable 的 list 資料類型,但 list 很像,但是唯讀
list = ['A', 'B']
tuple = ('A', 'B')
print(type(list)) # <class 'list'>
print(type(tuple)) # <class 'tuple'>
print(list) # ['A', 'B']
print(tuple) # ('A', 'B') 
這裡的示範有很大的危險,因為 list() 與 tuple() 都是 Python 內建的函式,因此若是拿 list 與 tuple  作為變數名稱,等於改了寫 list() 與 tuple() ,容易出現很難追蹤的問題,所以不只不能用 Python 的關鍵字當變數名稱,甚至 Python 內建函式的名稱最好也當作是「保留字」,不要去用

那為什麼不用 list,要用 tuple 呢?目前知道幾個地方適用 tuple。

第一,tuple 可以作為 dict 的 key(list 不行),因為 dict 使用 Hash 演算,所以 dict 的 key 必須是 Hashable,也就是 immutable。
d = {}
d['Neil', 'TP'] = '02', '12345678'
d['Neil', 'TC'] = '04', '12345678'
print(d) # {('Neil', 'TP'): ('02', '12345678'), ('Neil', 'TC'): ('04', '12345678')}
for first,last in d: # 同 d.keys()
    print(str(first) + ' ' + str(last) + ' > ' + str(d[first, last]))
# Neil TP > ('02', '12345678')
# Neil TC > ('04', '12345678')

for name, phone in d.items(): # 小心這個差異
    print(str(name) + ' > ' + str(phone))
# ('Neil', 'TP') > ('02', '12345678')
# ('Neil', 'TC') > ('04', '12345678')
第二,多重指定是使用 tuple 語法的概念。
d = { 'Name': 'Neil', 'Food': 'Banana'}
for k,v in d.items():
    print(str(k) + ' > ' + str(v))
# 上面語法可以改成
for (k,v) in d.items():
    print(str(k) + ' > ' + str(v))

a = 'Neil'
b = 'Banana'
b, a = a, b
print(a, b) # Banana, Neil
# 上面語法也可以改成
(b, a) = (a, b)
print(a, b) # Neil Banana
第三,函式一次回傳多個值,不必再用其他容器去裝,事實上是 Python 自動打包成 tuple 並利用多重指定再解開。

List 與 tuple 都是 item 的容器,就語法上,list 是用中括號,tuple 用小括號(非必要),一樣都是用逗號區隔 item。

因為 tuple 是唯讀,Python 可以對它做一些最佳化,效能和 list 比起來會好一些。

另外有一個特別的地方,在建立只有一個 item 的 tuple 時,要多加一個逗號,不然會被誤認成其他型別。
print(type((2))) # <class 'int'>
print(type((2,))) # <class 'tuple'>
print(type([2,])) # <class 'list'>
其實在 list 與 tuple 都是允許最後多一個逗號的。

正確來說建立 tuple 的語法,只要逗號,小括號不是必要的
t = (0, 1, 2, 3)
print(type(t)) # <class 'tuple'>
print(t) # (0, 1, 2, 3)
t = 0, 1, 2, 3
print(type(t)) # <class 'tuple'>
print(t) # (0, 1, 2, 3)

notANumber = 1,435 # 加了千分號的數字就不是 int 了,而是 tuple
print(notANumber) # (1, 435)

型別轉換

可以利用 list() 與 tuple() 在 list 與 tuple 間作型別轉換。
tuple1 = (0, 1, 2, 3)
print(tuple1) # (0, 1, 2, 3)
print(list(tuple1)) # [0, 1, 2, 3]
print(tuple(list(tuple1))) # (0, 1, 2, 3)
tuple() 和 list() 一樣,可以吃 sequence 做參數,而 sequence 目前知道的有 list、dict 與 str,對,就是 str。
t = tuple('Python')
print(t) # ('P', 'y', 't', 'h', 'o', 'n')

tuple 操作

所有 list 的唯讀操作,像是 index 與 slice 都可以用在 tuple 上。
tuple = (0, 1, 2, 3)
print(tuple[2]) # 2
print(tuple.index(3)) # 3
print(tuple[1:3]) # (1, 2)
也可以使用 +。
l = tuple('Py')
print(l) # ('P', 'y')
t = tuple('thon')
print(t) # ('t', 'h', 'o', 'n')
x = l + t
print(x) # ('P', 'y', 't', 'h', 'o', 'n')
比較運算子(>、>=、<、<=、==)也可以,逐個 item 比較直到分出結果。
l = tuple('Hello Python')
t = tuple('Htllo PYTHON')
print(l > t) # False
print(l < t) # True
由於 tuple 是 immutable,list 的 sort() 與 reverse() 這類會修改本身的 method 都不能用,但是可以用 Python 內建的函式 sorted() 與 reversed() 取代,這類內建的函式不會修改本身,而是回傳新的 tuple。
t = tuple('Python')
print(t) # ('P', 'y', 't', 'h', 'o', 'n')
t = sorted(t)
print(t) # ['P', 'h', 'n', 'o', 't', 'y']
t = reversed(t)
print(t) # <list_reverseiterator object at 0x018BF130>
print(', '.join(t)) # y, t, o, n, h, P

多重指定 Tuple assignment

前面已經有稍微看過多重指定,這裡在仔細說明一下。
a = 'Neil'
b = 'Banana'
b, a = a, b # 對調 a 與 b,不用 temp
兩邊的數目必須相同,右邊可以是任意 sequence
a, b, c = 'Hey'
print(a, b, c, sep = ', ') # H, e, y

a, b, c = list('Hey')
print(a, b, c, sep = ', ') # H, e, y

a, b, c = tuple('Hey')
print(a, b, c, sep = ', ') # H, e, y
多重指定 / tuple 可以用在一個方便的地方:一次回傳多個值
name, domain = 'cw1057@gmail.com'.split('@')
print(name, domain) # cw1057 gmail.com

t = divmod(5, 2)
print(t) # (2, 1)
q, r = divmod(5, 2)
print(q, r) #2 1

def find_ends(li):
    return min(li), max(li)
print(find_ends(list('993491684'))) # ('1', '9')
dict.items() 也是回傳 list of tuple,可以使用多重指定讀取。
d = {'Name': 'Neil', 'Food': 'Banana', 'Sport': 'Jogging'}
print(d) # {'Name': 'Neil', 'Food': 'Banana', 'Sport': 'Jogging'}
for k,v in d.items():
    print(str(k) + ' > ' + str(v))
# Name > Neil
# Food > Banana
# Sport > Jogging

dict、tuple 與 zip

可以使用 list of tuple 或者 zip 建立或修改 dict
# 用 list of tuple 建立 dict
d1 = dict([('Name', 'Neil'), ('Food', 'Banana'), ('Sport', 'Jogging')])
print(d1) # {'Name': 'Neil', 'Food': 'Banana', 'Sport': 'Jogging'}

# 用 zip 建立 dict
d2 = dict(zip(['Name', 'Food', 'Sport'], ['Neil', 'Banana', 'Jogging']))
print(d2) # {'Name': 'Neil', 'Food': 'Banana', 'Sport': 'Jogging'}

# 用 list of tuple 更新 dict
d1.update([('Food', 'Pineapple'), ('Learning', 'Python')])
print(d1) # {'Name': 'Neil', 'Food': 'Pineapple', 'Sport': 'Jogging', 'Learning': 'Python'}

# 用 zip 更新 dict
d2.update(zip(['Name', 'Like'], ['Emma', 'Sleep']))
print(d2) # {'Name': 'Emma', 'Food': 'Banana', 'Sport': 'Jogging', 'Like': 'Sleep'}

如何選擇 str、list 或 tuple?

個別特點如下:

  • str 的型別受限,就是 char,且 immutable
  • list  彈性最大,不限型別且為 mutable
  • tuple 不限型別,但是 immutable
在某些場合,tuple 比 list 好用(或適合):
  • 函式要回傳多個值
  • dict 的 key
  • 把許多參數打包成 tuple 傳給函式,可以避免參數過多與名稱衝突

---
---
---

沒有留言:

張貼留言