这一章讨论的内容有些你在之前已经看过,但我们会更深入的讨论。另外,我们也会介绍一些新的东西。
列(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