您的位置:首页 > 理论基础 > 数据结构算法

Python Tutorial第五章 数据结构

2006-12-11 21:20 507 查看
5. 数据结构

本章更详细地讲述了一些你已经知道的东西,还补充了一些新的材料。


5.1
更多关于列表

列表数据类型有更多的方法。以下是列表对象所有的方法:

append(

x)

Add an item to the end of the list; equivalent to a[len(a):] = [x].

extend(

L)

Extend the list by appending all the items in the given list; equivalent to a[len(a):] = L.

insert(

i, x)

Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).

remove(

x)

Remove the first item from the list whose value is x. It is an error if there is no such item.

pop(

[i])

Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)

index(

x)

Return the index in the list of the first item whose value is x. It is an error if there is no such item.

count(

x)

Return the number of times x appears in the list.

sort(

)

Sort the items of the list, in place.

reverse(

)

Reverse the elements of the list, in place.

一个使用以上大部分方法的例子:

>>> a = [66.25, 333, 333, 1, 1234.5]

>>> print a.count(333), a.count(66.25), a.count('x')

2 1 0

>>> a.insert(2, -1)

>>> a.append(333)

>>> a

[66.25, 333, -1, 333, 1, 1234.5, 333]

>>> a.index(333)

1

>>> a.remove(333)

>>> a

[66.25, -1, 333, 1, 1234.5, 333]

>>> a.reverse()

>>> a

[333, 1234.5, 1, 333, -1, 66.25]

>>> a.sort()

>>> a

[-1, 1, 66.25, 333, 333, 1234.5]


5.1.1
把列表当栈使用

列表的方法使得把列表当作后进先出(last-in, first-out)的栈使用变得很容易.append()向栈顶添加一个元素。不带参数的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]

5.1.2把列表当队列使用

你也可以很方便的把列表当成先进先出(first-in, first-out)队列使用。append()向队列尾部添加一个元素。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']

5.1.3 Functional Programming Tools

有三个对列表非常有用的函数:filter(), map() 和reduce().
"filter(function, sequence)"返回一个包含所有sequence中使function(item)为真的元素。如果序列是字符串或者元组,返回值也是字符串或元组。否则返回值将总是一个列表。例如下面这个计算部分素数的程序:
>>> 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)"对序列的每个元素调用function(item),并返回一个包含这些返回值的列表。例如下面求立方数的程序:
>>> def cube(x): return x*x*x

...

>>> map(cube, range(1, 11))

[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

map可以接受多于一个序列。对应的函数也要能接受和这些序列数同样多的参数。每次调用中每个序列的对应元素会被传给函数(如果一些序列比另一些短则传入None)。例如:
>>> seq = range(8)

>>> def add(x, y): return x+y

...

>>> map(add, seq, seq)

[0, 2, 4, 6, 8, 10, 12, 14]

"reduce(function, sequence)"返回一个值。该值由二元函数function在序列上迭代生成。即先将function作用在序列的前两个元素上,再作用到返回值和下一个元素上,以此类推。例如计算从1到10自然数的和:
>>> def add(x,y): return x+y

...

>>> reduce(add, range(1, 11))

55

如果序列只有一个元素则返回该元素。序列为空则引起一个异常。
可以传给reduce第三个参数以表示初值。这时初值相当于由一个空序列返回,函数首先作用于初值和序列的第一个元素,然后再作用在返回值和第二个元素上,以此类推。例如:
>>> def sum(seq):

...     def add(x,y): return x+y

...     return reduce(add, seq, 0)

...

>>> sum(range(1, 11))

55

>>> sum([])

0

不要使用这里的sum()定义:因为数列求和是一个常见的操作,Python已经提供了内置的函数sum(sequence),和例子中的sum几乎一样。内置的sum函数在Python2.3中开始出现。
5.1.4 List Comprehensions

List Comprehensions提供了一种不需要使用map(), filter()和/或lambda来创建列表的方法。它使得返回列表的定义比用map()等函数更清晰。每个list comprehension包含一个后面跟着for语句的表达式,for语句后可能有0个或多个for或者if语句。结果是一个列表,列表通过在满足后面for和if条件的元素上作用表达式而得到。如果表达式返回一个元组,则必须用圆括号括起来。
>>> 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] # error - parens required for tuples

  File "<stdin>", line 1, in ?

    [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]

>>> [vec1[i]*vec2[i] for i in range(len(vec1))]

[8, 12, -54]

List Comprehensions比map()更灵活,可以作用到复杂表达式和嵌套函数上:
>>> [str(round(355/113.0, i)) for i in range(1,6)]

['3.1', '3.14', '3.142', '3.1416', '3.14159']

5.2 del语句

有种方法可以让你从列表中按索引而不是按值删除一个元素:使用del语句。它不返回值,这点和pop()不一样。del还可以用来从列表中删除一个切片或者清空整个列表(也可以通过把空列表赋值给一个切片达到相同目的)。例如:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]

>>> del a[0]

>>> a

[1, 66.25, 333, 333, 1234.5]

>>> del a[2:4]

>>> a

[1, 66.25, 1234.5]

>>> del a[:]

>>> a

[]

del也可以用来删除整个变量:
>>> del a

此后所有对a的引用都会引发一个错误(除非另外给a赋值)。我们后面会发现del的其它用法。
5.3 元组和序列

我们发现列表和字符串有很多共同的性质,如索引和切片操作等。它们是序列数据类型的两种实例。Python是一种不断演化的语言,因此未来可能加入其它的序列数据类型。还有另外一种标准的序列数据类型:元组。

元组包含一些被逗号分开的元素,例如:
>>> 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))

正如你看到的,输出的元组会被圆括号围起来,因此可以正确的表示嵌套元组。输入的时候可以有或没有圆括号,尽管很多时候圆括号是必需的(作为表达式的一部分)。
元组有很多用途。例如:(x,y)坐标对,数据库中的雇员纪录等等。元组和字符串一样是不可变的,不能给元组中的某个元素赋值(你可以用切片和拼接达到类似的效果)。但是可以创建包含列表等可变对象的元组。
一个特殊的问题是创建包含0个或1个元素的元组:有一些特别的语法来达到这个目的。用一对空的圆括号构造一个空元组;在一个值后跟一个括号来创建包含一个元素的元组(仅仅用圆括号括起一个值是不够的).丑陋,但是高效。例如:
>>> empty = ()

>>> singleton = 'hello',    # <-- note trailing comma

>>> len(empty)

0

>>> len(singleton)

1

>>> singleton

('hello',)

语句t = 12345, 54321, 'hello!'是一个元组打包的例子:值12345,54321和'hello!'被打包到一个元组中。逆向操作也是可以的:
>>> x, y, z = t

这通常被称作序列解包(sequence unpacking)。序列解包需要左侧的变量数目和右侧序列包含的元素数目相同。请注意多赋值实际上只是元组打包和序列解包的叠加!
有一点不对称的是打包多个值总会得到元组,而解包则对任何序列都起作用。
5.4 集合(Sets)

Python也引入了一种集合数据类型。集合是无序且不含重复元素的容器。基本的用途包含成员测试,重复元素删除等。集合还支持并,交,差,对称差等集合运算。
看一个简单的例子:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

>>> fruit = set(basket)               # create a set without duplicates

>>> fruit

set(['orange', 'pear', 'apple', 'banana'])

>>> 'orange' in fruit                 # fast membership testing

True

>>> 'crabgrass' in fruit

False

 

>>> # Demonstrate set operations on unique letters from two words

...

>>> a = set('abracadabra')

>>> b = set('alacazam')

>>> a                                  # unique letters in a

set(['a', 'r', 'b', 'c', 'd'])

>>> a - b                              # letters in a but not in b

set(['r', 'd', 'b'])

>>> a | b                              # letters in either a or b

set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])

>>> a & b                              # letters in both a and b

set(['a', 'c'])

>>> a ^ b                              # letters in a or b but not both

set(['r', 'd', 'b', 'm', 'z', 'l'])

 

5.5 字典(Dictionaries)

Python中另一个有用的数据类型是字典。字典类似其他语言中的"关联内存"或者"关联数组"。和序列以整数索引不一样,字典用键(key)索引。键可以是任何不可变的数据类型,字符串和数字都可以作为键。仅仅包含字符串,数值和元组的元组也可以作键。如果元组直接或间接包含了可变对象,则不能作为键。你不能使用列表作为键,因为列表可以被元素赋值,切片赋值,或者append(),extend()等方法改变。
最好把字典想象成一组无序的键:值对的集合。同一个字典中的键不能重复。一对花括号创建一个空字典:{}.用花括号围起来的一组以逗号分开的键:值对为字典赋初值。这也是字典在终端输出的方式。
下面是一个使用字典的简单实例:
>>> 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')

True

>>> 'guido' in tel

True

dict构造函数直接从一列以二元元组方式表示的键值对上创建字典。当键值对有规律可循时,list comprehensions能简洁地指定键值列表。
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])

{'sape': 4139, 'jack': 4098, 'guido': 4127}

>>> dict([(x, x**2) for x in (2, 4, 6)])     # use a list comprehension

{2: 4, 4: 16, 6: 36}

稍后我们会学到General Expressions.它更适合于为dict()构造函数提供键值对。
当键是简单字符串时,有时用关键字参数指定键值对更简单:
>>> dict(sape=4139, guido=4127, jack=4098)

{'sape': 4139, 'jack': 4098, 'guido': 4127}

 

5.6 循环技术

在字典上循环时,键和对应的值可以通过iteritems()方法获取。
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}

>>> for k, v in knights.iteritems():

...     print k, v

...

gallahad the pure

robin the brave

在序列上循环时,位置索引和对应的值可以通过enumerate()函数同时获取。
>>> for i, v in enumerate(['tic', 'tac', 'toe']):

...     print i, v

...

0 tic

1 tac

2 toe

同时在两个或多个序列上循环时,可以通过zip()函数来把对应项组成对。
>>> questions = ['name', 'quest', 'favorite color']

>>> answers = ['lancelot', 'the holy grail', 'blue']

>>> for q, a in zip(questions, answers):

...     print 'What is your %s?  It is %s.' % (q, a)

...

What is your name?  It is lancelot.

What is your quest?  It is the holy grail.

What is your favorite color?  It is blue.

反向遍历一个序列需要先指定正向序列,再调用reversed()函数。
>>> for i in reversed(xrange(1,10,2)):

...     print i

...

9

7

5

3

1

在一个序列上按一定顺序遍历时,使用sorted()函数。它会返回一个新的排好序的列表而不改变原有序列。
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

>>> for f in sorted(set(basket)):

...     print f

...        

apple

banana

orange

pear

 

5.7 更多关于条件

while和if语句中使用的条件可以包含任意操作符而不仅是比较。
比较操作符in和not in测试一个值是否出现在一个序列中。操作符is和is not比较两个对象是否是相同的对象。这只影响可变对象如列表(译者注:不变对象也能用is和is not判断)。所有的比较操作符有相同的优先级,比所有的算数操作符都低。
比较可以传递,例如,a < b == c测试a < b 并且 b == c
比较可以用布尔运算符and和or组合。比较以及任意布尔表达式的结果可以用not修饰表示取反。布尔运算符比比较运算符优先级低。布尔运算符中not的优先级最高,or的优先级最低。所以 A and not B or C 等于 (A and (not B)) or C.和通常一样,圆括号用来表示需要的顺序组合。
布尔操作符and和or也被称作短路操作符:它们的参数从左到右依次求值,当结果可以确定时求值终止。例如, 假定A和C为真但B为假,A and B and C 不会计算表达式C的值。在用作普通值而非布尔值时,断路操作符返回最后一个求值的结果。
可以将比较结果或其它布尔表达式赋给一个变量。例如:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'

>>> non_null = string1 or string2 or string3

>>> non_null

'Trondheim'

请注意Python中不能在表达式里赋值,和C不一样。C程序员可能对此发牢骚。但这样避免了C程序中常见的一类错误:在需要==的时候输入了=。
 

5.8 比较序列和其它类型

序列对象可以和同类型的序列对象进行比较。比较按字典序进行:首先比较第一个元素,如果不一样即为比较结果。如果第一个元素相等则比较第二个元素,以此类推,直到一个序列终止。如果两个相比较的元素是同类型的序列,则递归进行字典序比较。如果两个序列所有元素都相等,则序列相等。如果一个序列是另一个序列从头开始的一段子序列,则较短的那个是较小的一个。字符串的字典序比较使用ASCII值对字符进行比较。下面是一些同类型序列比较的例子:
(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)总小于一个字符串(string),一个字符串总小于一个元组,依次类推[1]。不同数值类型的比较则按照其数值进行。因此0等于0.

[1] 你不能依赖这条序列比较规则。它们在语言的未来版本中可能改变。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息