想想你平時撰寫的一些應用程式,大部份是在處理一組資料,Python 對管理資料用的容器(Container)型態,在語法上提供直接支援,加上
for
包含式(comprehension)的支援,在資料處理問題上可獲得不少的便利性。 Python 支援的容器型態有
list
、set
、dict
、tuple
等。
list 型態
list
是有序且可變群集(Collection),在 Python 中,[1, 2, 3]
這樣的語法,即可建立含元素 1、2、3 而索引 0、1、2 的 list
實例。
list
與先前介紹過的 string
享有共同的操作。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
型態是無序群集,管理的元素不會重複而且必須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()
[('Hamimi', 970221), ('Justin', 123456)]
>>> passwords.keys()
['Hamimi', 'Justin']
>>> passwords.values()
[970221, 123456]
>>>
使用
[]
時如果指定的鍵不存在,會發生 KeyError
,可以使用 dict
的 get
方法,指定鍵不存在時傳回的預設值。例如:
>>> passwords.get('openhome', '000000')
'000000'
>>> passwords['openhome']
Traceback (most recent call last):
File "", line 1, in
KeyError: 'openhome'
>>>
tuple 型態
tuple
型態作用類似 list
,不過 tuple
實例是不可變(Immutable),也就是一旦建立,無法對其增減元素。除了增減元素個數之外,tuple
與 list
操作上類似,事實上,循序結構的物件(像是字串、list
、tuple
等),在 Python 中共享某些操作方式。管理物件時該使用可變物件還是不可變物件?不可變物件在某些情況下,會擁有較好的效能,之後還會談到更多有關不可變物件的好處。 (在靜態定型的 Haskell 語言中,Tuple 更具效用,因為 Tuple 中的元素型態就組成了一個新的但未命名的型態。)
練習 3:Python 互動模式與直譯器指令
開啟一個終端機,鍵入
python
指令進入互動模式,接著鍵入以下指令,你會看到什麼?
- 1 + 2
_
#_
代表最後一次執行結果- _ + 3
help()
# 進入文件查詢介面len
# 查詢len
函式說明keywords
# 查詢關鍵字有哪些quit
(或是q)
# 離開文件查詢介面help(len)
# 直接查詢len
函式說明Ctrl + D
# 離開互動模式
- python -h
- python -c 'print "Hello! Python!"'
- python -c 'help(len)'
- python -c 'import this'
-h
引數會顯示使用說明,-c
會直譯後續給定的字串文字,因此 python -c 'help(len)'
就是顯示 len
函式的說明,import this
會顯示一段文件,代表 Python 的哲學,也是發展的規範:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
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(raw_input('Number 1: '))
n = int(raw_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']}
set(['caterpillar', 'Justin', 'openhome'])
>>>
也可以建立
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
包括式,就比較能夠接受這樣的寫法。) 練習 4:使用
for
包含式 在 LAB 檔案中,有個 exercises\exercise4\exercise4-1.py,內容如下:
numbers = []
for number in range(20):
numbers.append(str(number))
print ", ".join(numbers)
請試著使用
for
包含式來改寫它,解答可在 LAB 檔案的 solutions\exercise4\exercise4-1.py 找到。 (如果想挑戰比較難的練習,可試著使用
for
包含式來求解以下問題:找出周長為 24,每個邊長都為整數且不超過 10 的直角三角形。你可以試著看看 LAB 檔案中 exercises\exercise4\exercise4-2.py 的提示,解答可在 LAB 檔案的 solutions\exercise4\exercise4-2.py 找到)