想想你平時撰寫的一些應用程式,大部份是在處理一組資料,Python 對管理資料用的容器(Container)型態,在語法上提供直接支援,加上 for
包含式(comprehension)的支援,在資料處理問題上可獲得不少的便利性。
Python 支援的容器型態有 list
、set
、dict
、tuple
等。
list
型態
list
是有序且可變群集(Collection),在 Python 中,[1, 2, 3]
這樣的語法,即可建立含元素 1、2、3 而索引 0、1、2 的 list
實例。
list
與先前介紹過的 str
享有共同的操作。len
傳回 list
長度;in
可測試某元素是否在 list
中;+
可以用來串接兩個 list
;*
可用來複製出指定數量的 list
。[]
可以指定索引,用以從 list
中取得元素,負索引是從最後一個元素計數,使用 []
來切割 list
或許是最有用的功能。其他操作還有…
>>> [0] * 10
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> ', '.join(['justin', 'caterpillar', 'openhome'])
'justin, caterpillar, openhome'
>>> list('justin')
['j', 'u', 's', 't', 'i', 'n']
>>>
set
型態
set
型態是無序群集,管理的元素不會重複而且必須是 hashable(這中間還有一些要討論的東西,可以參考〈物件相等性(上)〉)。以下是 set
的幾個功能示範:
>>> admins = {'Justin', 'caterpillar'} # 建立 set
>>> users = {'momor', 'hamini', 'Justin'}
>>> 'Justin' in admins # 是否在站長群?
True
>>> admins & users # 同時是站長群也是使用者群的?
{'Justin'}
>>> admins | users # 是站長群或是使用者群的?
{'hamini', 'caterpillar', 'Justin', 'momor'}
>>> admins - users # 站長群但不使用者群的?
{'caterpillar'}
>>> admins ^ users # XOR
{'hamini', 'caterpillar', 'momor'}
>>> admins > users # ∈
False
>>> admins < users
False
>>>
dict
型態
鍵(Key)值(Value)對應的物件,鍵物件必須是 hashable。以下是一些操作示範:
>>> passwords = {'Justin' : 123456, 'caterpillar' : 933933}
>>> passwords['Justin']
123456
>>> passwords['Hamimi'] = 970221
>>> passwords
{'caterpillar': 933933, 'Hamimi': 970221, 'Justin': 123456}
>>> del passwords['caterpillar']
>>> passwords
{'Hamimi': 970221, 'Justin': 123456}
>>> passwords.items()
dict_items([('Hamimi', 970221), ('Justin', 123456)])
>>> passwords.keys()
dict_keys(['Hamimi', 'Justin'])
>>> passwords.values()
dict_values([970221, 123456])
>>>
在 Python 2.x 中,dict
實例的 items
、keys
、values
會傳回 list
,而在 Python 3.x 中,它們會傳回 dict_items
、dict_keys
、dict_values
,這些物件是可以進行迭代的(例如使用 for...in
進行迭代),相較於建立一個夠長的 list
來儲存這些元素,Python 3.x 的作法比較經濟,特別是在 dict
中儲存的鍵值對很多的時候。
如果你真的需要一個 list
,那麼可以使用像是 list(passwords.items())
、list(passwords.keys())
、list(passwords.values())
的方式來取得。
使用 []
時如果指定的鍵不存在,會發生 KeyError
,可以使用 dict
的 get
方法,指定鍵不存在時傳回的預設值。例如:
>>> passwords.get('openhome', '000000')
'000000'
>>> passwords['openhome']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'openhome'
>>>
tuple
型態
tuple
型態作用類似 list
,不過 tuple
實例是不可變(Immutable),也就是一旦建立,無法對其增減元素。除了增減元素個數之外,tuple
與 list
操作上類似,事實上,循序結構的物件(像是字串、list
、tuple
等),在 Python 中共享某些操作方式。管理物件時該使用可變物件還是不可變物件?不可變物件在某些情況下,會擁有較好的效能,之後還會談到更多有關不可變物件的好處。
在靜態定型的 Haskell 語言中,
Tuple
更具效用,因為Tuple
中的元素型態就組成了一個新的但未命名的型態。
if
、for
與 while
流程語法中最簡單的 if..else
分支判斷,在 Python 中是這樣寫的:
from sys import argv
if len(argv) > 1:
print('Hello,', argv[1])
else:
print('Hello, Guest')
在 Python 中,使用 : 來做為區塊(Block)開始的標示,相同縮排則表示了相同區塊範圍的程式碼,縮排必須一致,如果想使用四個空白字元縮排,整個程式都必須是四個空白字元縮排,如果要用 Tab 縮排,整個程式都必須使用 Tab 縮排。Python 中的 if..else
也有運算式(Expression)形式,使用上就像是 C 或 Java 的三元運算子 ?:。if 條件式成立的話,會傳回 if
左邊的值,否則傳回 else
右邊的值。例如上面的程式也可以寫為:
from sys import argv
print('Hello,', argv[1] if len(argv) > 1 else 'Guest')
Python 中的 for
可用來迭代循序結構的物件。例如想將某 list
的元素都做二次方運算,收集在另一個 list
中的話,可以如下:
numbers = [10, 20, 30]
squares = []
for number in numbers:
squares.append(number ** 2)
print(squares)
至於 while
,一般是用在結束條件不確定的情況下。例如求最大公因數可以如下:
print('Enter two numbers...')
m = int(input('Number 1: '))
n = int(input('Number 2: '))
while n != 0:
r = m % n
m = n
n = r
print('GCD: {0}'.format(m))
回頭看看這個範例:
numbers = [10, 20, 30]
squares = []
for number in numbers:
squares.append(number ** 2)
print(squares)
將某 list
的元素都做二次方運算,收集在另一個 list
中的話,其實還可以使用 for
包含式:
numbers = [10, 20, 30]
print([number ** 2 for number in numbers])
這樣的寫法顯然簡潔多了。for
包含式也可以與條件式結合。例如想收集某個 list
中的奇數元素至另一 list
,單純使用 for
迴圈,可以如下:
numbers = [11, 2, 45, 1, 6, 3, 7, 8, 9]
odd_numbers = []
for number in numbers:
if number % 2 != 0:
odd_numbers.append(number)
print(odd_numbers)
使用 for
包含式的程式碼就簡潔多了:
numbers = [11, 2, 45, 1, 6, 3, 7, 8, 9]
print([number for number in numbers if number % 2 != 0])
for
包含式(comprehension)也可以形式巢狀結構,例如有個元素都為 list
的 list
,想將其中的 list
元素串起來,也就是將之平坦化,可以如下:
lts = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print([ele for lt in lts for ele in lt])
當你使用 []
包圍住 for
包含式(comprehension) 時,會建立 list
實例,如果使用 {}
的話,可以建立 set
實例,重複的元素會自動去除。例如:
>>> {name for name in ['caterpillar', 'Justin', 'caterpillar', 'openhome']}
{'Justin', 'openhome', 'caterpillar'}
>>>
也可以建立 dict
實例。例如:
>>> names = ['caterpillar', 'justin', 'openhome']
>>> passwds = [123456, 654321, 13579]
>>> {name : passwd for name, passwd in zip(names, passwds)}
{'justin': 654321, 'openhome': 13579, 'caterpillar': 123456}
>>>
上例中的 zip
函式,就如名稱意義,會將兩個 list
像拉鏈一樣,兩兩相扣在一起為 tuple
,這些 tuple
元素組成一個新的 list
,對於 tuple
元素組成的這個 list
,每個 tuple
中的一對元素再指定給 name
與 passwd
,最後這對 name
與 passwd
組成 dict
的一對鍵值。
有些人剛接觸 Python 時,不太習慣
for
包含式的寫法,可以來看看 Haskell 中如何表達數學式S = { 2 . x | x ∈ N, x ≦ 10}
,Haskell 是寫為[2 * x | x <- N, x <= 10]
,跟原本的數學式很相像,用這個方向來理解 Python 的for
包括式,就比較能夠接受這樣的寫法。
練習 3:使用 for
包含式
在 LAB 檔案中,有個 exercises\exercise3\exercise3-1.py,內容如下:
numbers = []
for number in range(20):
numbers.append(str(number))
print(", ".join(numbers))
請試著使用 for
包含式來改寫它,解答可在 LAB 檔案的 solutions\exercise3\exercise3-1.py 找到。
(如果想挑戰比較難的練習,可試著使用 for
包含式來求解以下問題:找出周長為 24,每個邊長都為整數且不超過 10 的直角三角形。你可以試著看看 LAB 檔案中 exercises\exercise3\exercise3-2.py 的提示,解答可在 LAB 檔案的 solutions\exercise3\exercise3-2.py 找到)