這一章討論的內容有些你在之前已經看過,但我們會更深入的討論。另外,我們也會介紹一些新的東西。
列(list)這個資料型態有一些方法可以使用,底下我們就列出來一些常用的方法:
append(x)
a[len(a):] = [x]
。
extend(L)
a[len(a):] = L
。
insert(i, x)
a.insert(0, x)
會加入在list的最前端,而 a.insert(len(a), x)
會在最後端加入,相等於 a.append(x)
。
remove(x)
x
. 的成員。如果整個list都沒有這個成員,那就會得到一個錯誤(error)。
pop([i])
a.pop()
會傳回這個list的最一個成員,同樣的這個成為會被從這個list之中拿掉。
index(x)
x
的成員之位置(index),如果整個list都沒有這個成員,那就會得到一個錯誤(error)。
count(x)
x
出現了多少次。
sort()
reverse()
底下的這個例子使用了大部分的lsit的方法(method):
>>> a = [66.6, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.6), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.6, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.6, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.6]
>>> a.sort()
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]
由於有這些好用的方法,把列(list)當作堆積(stack)來使用是一件容易的事。Stacks的最後一個加入的成員是第一個被取出來的成員(後進先出``last-in, first-out''法則)。要在stack的最頂端加入一個成員可以使用 append() ,要從stack的最頂端取出一個成員可以用 pop() (不須加入參數)。例子如下:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
你也可以很方便的拿list來當作佇列(queues)來使用。Queues的特點是第一個加入的成員會是第一個被取出的成員(先進先出``first-in,
first-out''法則)。要在queue的後端加入一個成員可以使用 append() ,要從queue的最前端取出一個成員可以使用
use pop() ,記得參數是 0
。例子如下:
>>> queue = ["Eric", "John", "Michael"]
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.pop(0)
'Eric'
>>> queue.pop(0)
'John'
>>> queue
['Michael', 'Terry', 'Graham']
有三個與list合用非常有用的內建工具函式: filter(), map(), 以及 reduce() 。
"filter(
function, sequence)" 這個函式會傳回 一個sequence (如果可能的話其成員為同一資料型態),這個sequence裡面的成員都是將
sequence 裡面的的成員,一一傳入到
function(
item)
所代表的函式後,傳回值為true的成員所組合而成。這個函式對於傳入的 sequence
有過濾的效果,如下例所示:
>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]
"map(
function, sequence)" 會針對 sequence 裡的各個成員呼叫
function(item)
,然後傳回個別成員呼叫之後傳回的結果。舉例來說,要計算一連串的立方值,我們可以如此做:
>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
我們也可以傳入不只一個sequence。如果傳入多個sequence的話,第一個函式名稱的參數要是能夠處理多個參數的函式,然後系統會把各個sequence相對應的成員拿出來放入函式之中(如果兩個sequence長度不相等的話,不足的會用
None
來補足)。如果第一個函式名稱參數為
None
的話,所呼叫的函式就僅僅是傳回其所傳入的參數。
綜合以上的兩個特性,我們可以使用 " map(None, list1, list2)" 這一個工具函式來方便的轉換兩個sequence成為一個成對的成員組合的sequence。請看例子:
>>> seq = range(8)
>>> def square(x): return x*x
...
>>> map(None, seq, map(square, seq))
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]
"reduce( func, sequence)" 會利用 sequence 的前兩個成員當參數呼叫 func ,然後所得的傳回值再與下一個成員當參數傳入 func ,一直到整個 sequence 結束。下面的例子計算1到10的總和:
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55
如果在 sequence 裡面只有一個成員的話,這個成員的值就會直接傳回。如果在 sequence 裡面沒有任何成員的話,會造成一個例外狀況(exception)。
我們也可以加入第三個參數來當作開始的值,如此當傳入的 sequence 是空的話,就可以使用這個開始值。如果是正常的sequencde的話,開始值會先跟第一個成員被傳入當作呼叫 func 的參數,其傳回值再跟第二個成員傳入 func ,依此類推。請看下例:
>>> def sum(seq):
... def add(x,y): return x+y
... return reduce(add, seq, 0)
...
>>> sum(range(1, 11))
55
>>> sum([])
0
List comprehensions提供了一個製造list簡潔的方法,而不用使用 map(), filter() 以及/或者 lambda 形式。其結果也比使用以上的方法來做出list要來的清楚易懂。list comprehension通常是一個expression跟著是一個 for 的語句,然後是零個或多個 for 或是 if 語句。其傳回的list是一個由在 for 及 if 語句條件下執行expression的結果。如果expression的結果是一個tuple,就必須用括號"( )"括起來。
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]
>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
>>> [{x: x**2} for x in vec]
[{2: 4}, {4: 16}, {6: 36}]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec] # error - parens required for tuples
File "<stdin>", line 1
[x, x**2 for x in vec]
^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec]
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
del 敘述可以讓你輕鬆的去掉在list當中某一個位置(index)的成員。這個敘述也可以用切割(slice)的方法來去掉某一段的成員(在之前我們必須藉著設定某個slice為空list來達成同樣的效果)。請看下例:
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.6, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.6, 1234.5]
del 也可以用來去掉整個變數:
>>> del a
如果你在去掉之後還繼續使用這個變數名稱的話,就會得到一個錯誤 (除非你之後再設定另外一個值給它)。我們稍後會繼續看到使用 del 的例子。
我們之前所討論的lists以及字串(strings)有很多的共通點,例如可以用index來定位置,可以切出其中的某一段(slicing)等等。事實上,list及字串都是 sequence 這個資料型態的特例。由於Python是一個可以不斷進步的語言,其他的sequence資料型態有可能會陸續的加入。我們就來看另外一種標準的sequence資料型態:固定有序列( tuple )。
一個tuple是由特定數目的值所組成,其成員與成員之間以逗號分開。舉例如下:
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
如同在前面例子所見到的,tuples輸出的結果都會包含在括弧之中。所以,巢狀tuple(tuple之中有tuple)可以被清楚的區分出來。Tuple在輸入的時候可以有括弧也可以沒有,通常我們都會加上括弧(特別是用在在複雜的expression之中)。
Tuples有很多種用途,例如(x, y)座標,從資料庫取出的員工的資料庫記錄等等。Tuples跟字串一樣都是不可改變(immutable)的:我們不能單獨的設定一個tuple裡面的個別成員(雖然我們可以用切割及連結(concaatenation)來達到同樣的效果)。我們也可以做出可以改變成員的tuple來,例如list。
有一個特殊的情況就是只包含0個或1個成員的tuple:要創造這樣的一個tuple,我們必須在語法上有一些的變化。空的tuple的表示方法是一對空的括弧,只有一個成員的tuple表示方法是在成員後面加上逗點(不能只是用括弧把一個成員括起來)。雖然有點奇怪,但是蠻有用的。請看例子:
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
t
= 12345, 54321, 'hello!'
這個敘述是一個tuple包裝( tuple packing )的例子:
12345
, 54321
以及 'hello!'
這三個值都被包裝放在一個tuple裡面了。我們也可以使用相反的操作方式,例如:
>>> x, y, z = t
這個動作叫做打開sequence( sequence unpacking )。Sequence unpacking的動作需要在設定符號左邊有一串的變數,其數目應與右邊sequence的成員數目相同。值得注意的是,多重設定(a, b = 1, 2)其實只是tuple packing以及sequence unpacking的結合罷了!
有一個不太對稱的地方:packing的動作永遠結果是一個tuple,但是unpacking可以針對各種不同的sequence來做。
另外一個在Python當中很好用的內建資料型態是字典( dictionary )。Dictionary有的時候在別的程式語言裡面也叫做連結記憶( ``associative memories'' )或者是連結陣列( ``associative arrays'' )。不同於sequence是由一連串的數字來做index,dictionary用一個特殊的不可改變的(immutable)鑰( keys 來當作其 index。字串及數字都不能被改變,所以都可以來當作dictionary的key。Tuple如果只含有字串,數目字,以及其他tuple的話也可以當作key。如果tuple裡面有包含任何可改變的(mutable)的物件的話(包括直接或間接),就不能當作key來使用。List不能當作key,因為list的成員可以被改變(你可以用 append() 以及 extend() 之類的方法,或是切割(slicing) 或 index 來設定list的個別成員)。
我們最好把dictionary想像成一個沒有順序的
key: value 成對的組合。唯一的條件是,在dictionary裡面key的值必須是唯一不重複的。最簡單的dictionary就是一對空的中括弧:
{}
。在中括弧裡面放入由逗號分開的key:value對,就成了dictionary裡面的成員。這也是當dictionary被印到輸出時的標準格式。
我們可以對dictionary做一些事,包括加入一個帶有key的值、或者是用key來找一個特殊的值。我們也可以用
del
來刪去一對key:value的成員。如果你試圖存一對key:value但是這個key已經被使用了的話,原來的那一個value的值就會被蓋過。如果你想用一個不存在的key來找出某一個成員的話,你會得到一個error。
使用
keys()
這一個dictionary的方法我們可以得到一個由所有的key值組成的list,其順序是隨機沒有次序的(如果你想要排序的話,只要針對這一個得到的list來呼叫其
sort()
方法就可以了)。要檢查某個key是不是存在的話,你可以使用 has_key()
這一個method來做檢查。
底下是一個有關dictionary的小例子:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
1
在之前所談到的
while
及 if
裡面的條件敘述,除了一般的比較之外也可以包含其他的運算。
我們可以用
in
以及 not in
來檢查某個值是否出現(或沒出現)在某個sequence裡面。我們也可以使用 is
以及 is not
來檢查兩個物件是否事實上指的是相同的一個物件(這只有跟像是list一樣可變的物件有關)。所有的比較運算的運算優先次序都是一樣的,都比所有的數字運算要來的低。
比較運算是可以連起來的:像是
a
< b == c
就是試驗是否 a
比 b
小,以及 b
和 c
是否相等。
比較運算也可以用
and
以及 or
等boolean運算來連結起來,其比較的結果(或其他boolean運算的結果)也可以用 not
來得到相反(negated)的結果。在這些運算裡, not
有最高的優先次序, or
的優先次序最低,但是它們所有的優先次序都比比較運算來的低。所以,
A and not B or C
其實相等於 (A and (not B)) or C
。當然,最好適時的使用括弧來幫助你表達你真正想要的組合。
and
以及 or
這兩個boolean運算元也可以稱做有捷徑的運算元( shortcut
operators):它們的evaluated的次序都是由左而右,而且一但已經可以決定其運算的結果,就不會再繼續的做下去。也就是說如果
A
以及 C
都是 true 而 B
是false的話, A and
B and C
並不會evaluate C 這個expression。一般來說這些shortcut operator 的傳回值如果不是當作boolean而是當作一般的值來用的話,其傳回值會是最後一個被evaluate的expression的值。
我們也可以把一個比較運算,或是 boolean運算的結果設定給一個變數,其例子如下:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
值得注意的是不像是C,在Python裡面設定(assignment)不能夠放在expression裡面。C的程式設計師也許會針對此點抱怨,但是這樣的好處是可以避免一些常見的把設定(
=
)及等於( ==
)弄混淆的情形
Sequence 物件可以和其他的相同資料型態的sequence 物件相比較,其比較方法是依照所謂的 lexicographical 順序(lexicographical ordering)。首先是兩個sequence的第一個成員互相比較,如果比較有大小之別的話就此決定其相對大小,若是相等的話就再比較下一個成員的大小,餘此類推直到sequence的結束。如果兩個要相比較的成員本身也是一個sequence的話,同樣的條件可以繼續遞迴的使用在這兩個sequence之上。如果這兩個sequence的所有成員都相等的話,我們就說這兩個成員是相等的。如果某一個sequence是另一個sequence的一部份的話,較短的那一個sequence就是較小的。字串的Lexicographical順序用的是個別字元的ASCII碼的順序。底下是一些同一資料型態的sequence的比較例子:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
值得注意的是,我們也可以 比較兩個不同資料型態的物件,而其結果是依其資料型態的名稱來決定的。所以所有的list都比字串還要來的小(因為list小於string),所有的string也都比tuple還要小。至於數值的資料型態則是由其數值大小來決定其大小,所以0等於0.0的,其餘按此類推。 5.1