[学]Python用户手册笔记_2_Data Structures

Data Structures
数据结构

4 数据结构

4.1 深入链表

append(x)   把一个元素添加到链表的结尾。

extend(L)   通过添加指定链表的所有元素来扩充链表。

insert(i,x) 在指定位置插入一个元素。

remove(x)   删除链表中值为x的第一个元素。如果没有这样的元素,就会返回一个错误。

pop([i])    从链表的指定位置删除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从链表中被删除。

index(x)    返回链表中第一个值为x的元素的索引。如果没有匹配的元素就会返回一个错误。

count(x)   返回x在链表中出现的次数。

sort()     就地对链表中的元素进行排序。

reverse()  就地对链表中的元素进行倒排序。

4.1.1 把链表当作堆栈使用

链表方法使得链表可以很方便的作为一个堆栈来使用。堆栈为后进先出。用append()方法可以把一个元素添加到堆栈顶。用不指定索引的pop()方法可以把一个元素从堆栈顶释放出来。

>>> stack = [3, 4, 5]

>>> stack.append(6)

>>> stack

[3, 4, 5, 6]

>>> print stack

[3, 4, 5, 6]

>>> stack.pop()

6

>>> stack

[3, 4, 5]

>>>  


4.1.2
把链表当作队列使用

也可以把链表当作队列使用,队列作为特定的数据结构,最先进入的元素最先释放。使用append()方法可以把元素添加到队列最后,以0为参数调用pop()方法可以把最先进入的元素释放出来。

>>> queue = ["Eric", "John", "Michael"]

>>> queue.append("Terry")

>>> queue.append("Graham")

>>> queue

['Eric', 'John', 'Michael', 'Terry', 'Graham']

>>> queue.pop(0)

'Eric'

>>> queue

['John', 'Michael', 'Terry', 'Graham']

>>>  


4.1.3
函数化编程工具

链表中有三个内置函数非常有用:filter(),map()reduce()

filter(function, sequence)返回一个sequence,包括了给定序列中所有调用function后返回值为true的元素。如果sequence是一个string或者tuple,返回值必行是同一类型,否则,它总是list

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

>>>

可以传入多个序列,函数也必须要有对应数量的参数,执行时会依次用各序列上对应的元素来调用函数。如果某些序列比其它的短,就用None来代替。如果把None做为一个函数传入,则直接返回参数做为替代。

>>> seq = range(8)

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

...

>>> map(add, seq, seq)

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

>>>

reduce(func, sequence)返回一个单值,它是这样构造的:首先以序列的前两个元素调用函数,再以返回值和第三个参数调用,依次执行下去。

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

...

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

55

>>>

如果序列中只有一个元素,就返回它,如果序列是空的,就抛出一个异常。

可以传入第三个参数做为初始值。如果序列是空的,就返回初始值,否则函数会先接收初始值和序列的第一个元素,然后是返回值和下一个原色,依次类推。例如:

>>> def sum(seq):

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

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

...

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

55

>>> sum([])

0

>>>


4.1.4
链表推导式

链表推导式提供了一个创建链表的简单途径,无须使用map(),filter()以及lambda。返回链表的定义通常比创建这些链表更清晰。每一个链表推导式包括一个for语句之后的表达式,零或多个forif语句。返回值是由forif字句之后的表达式得到的元素组成的链表。如果想得到一个元组,必须要加上括号。

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

[]

>>> [3*x for x in vec if x<4]

[6]

>>> [[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,x*2] for x in vec]

[[2, 4], [4, 8], [6, 12]]

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

>>>

链式推导式比map()更复杂,可使用复杂的表达式和嵌套函数。

>>> [str(round(355/113.0,i)) for i in range(1,6)]

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

>>>


4.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]

>>> a

[1, 66.25, 333, 1234.5]

>>> del a[2:4]

>>> a

[1, 66.25]

>>>

del也可以用于删除整个变量:

>>> del a

>>> a

Traceback (most recent call last):

  File "<interactive input>", line 1, in <module>

NameError: name 'a' is not defined

>>>

此后再引用这个名字会发生错误(至少要到给它赋另外一个值为止)。

4.3 元组和序列

链表和字符串有很多通用的属性,如索引和切割操作。它们是序列类型中的两种。

有一种标准序列类型:元组。一个元组由数个逗号分隔的值组成。

>>> t = 12345, 54321, 'hello'

>>> t[0]

12345

>>> t

(12345, 54321, 'hello')

>>> u = t, (1, 2, 3)

>>> u

((12345, 54321, 'hello'), (1, 2, 3))

>>>

元组也可能是容器

元组在输出时总是有括号的,以便于正确表达嵌套结构。在输入时可能有或没有括号都可以,不过经常括号都是必须的。

元组由很多用途。例如(x,y)坐标点,数据库中的员工记录等等。元组就像字符串,不可改变:不能给元组的一个独立的元素赋值(尽管可以通过联接和切割来模仿)。也可以通过包含可变对象来创建元组,例如链表。

构造零个元素的元组,使用空的括号创建。构造有一个单元素元组可以在值后面跟一个逗号。例如:

>>> empty = ()

>>> singletuple = 'hello',

>>> len(empty)

0

>>> len(singletuple)

1

>>> singletuple

('hello',)

>>>

语句t = 123, 321, 'niky'是元组封装的一个例子:值123,321,‘niky’被封装进元组。其逆操作可能是这样:

>>> t = 123, 321, 'niky'

>>> x, y, z = t

>>> x

123

>>> y

321

>>> z

'niky'

>>>

这个调用被称为序列拆封非常合适。序列拆封要求左侧的变量数目与序列的元素个数相同。要注意的是可变参数其实只是元组封装和序列拆封的一个结合。

这里有一点不对称:封装多重参数通常会创建一个元组,而拆封操作可以作用于任何序列。

4.4 集合

Python还包含了一个数据类型set。集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持unionintersectiondifferencesysmmetric difference(对称差集)等数学运算。

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

>>> fruit = set(basket)

>>> fruit

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

>>> 'orange' in fruit

True

>>> # 演示集合操作

>>> a = set('abracadabra')

>>> b = set('alacazam')

>>> a

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

>>> b

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

>>> a - b # a中有,而b中没有

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

>>> a | b # a 或者 b

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

>>> a & b

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

>>> a ^ b # ab中,但不会同时在ab

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

>>>


4.5
字典

字典在某些语言中可能称为“联合内存”或“联合数组”。序列是以连续的整数位索引,与此不同的是,字典以关键字为索引,关键字可以是任意不可变类型,通常用字符串或数值。如果元组中只包含字符串和数字,它可以做为关键字,如果它直接或间接的包含了可变对象,就不能当作关键字。不能用链表做关键字,因为链表可以用索引、切割或者append()extend()等方法改变。

字典可以看做是无序的关键字:值对集合,关键字必须是互不相同的。一对大括号创建一个空的字典{}。初始化链表时,在大括号内放置一组逗号分隔的关键字:值对,这也是字典输出的方式。

字典的主要操作是依据关键字来存储和析取值。也可以用del来删除关键字:值对。如果用一个存在的关键字存储值,以前为该关键字分配的值会被遗忘。试图从一个不存在的关键字中析取值会导致错误。

keys()返回由所有关键字组成的链表,该链表顺序不定。使用字典的has_key()方法或in关键字可以检查字典中是否存在某一关键字。

>>> tel = {'jack':4088, 'sivan':8888}

>>> tel

{'sivan': 8888, 'jack': 4088}

>>> tel['niky'] = 6666

>>> tel

{'sivan': 8888, 'niky': 6666, 'jack': 4088}

>>> tel['niky']

6666

>>> del tel['jack']

>>> tel

{'sivan': 8888, 'niky': 6666}

>>> tel.keys()

['sivan', 'niky']

>>> tel.has_key('niky')

True

>>> 'niky' in tel

True

>>>

链表中存储关键字-值对元组的话,dict()可以从中直接构造字典。关键字-值对来自某个特定模式时,可以用链表推导式简单的生成关键字-值链表。

>>> dict([('sape',4139),('guido',4127),('jack',4098)])

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

>>> dict([(x,x**2) for x in (2,4,6)])

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

>>> [(x,x**2) for x in (2,4,6)]

[(2, 4), (4, 16), (6, 36)]

>>>

使用简单字符串作为关键字的话,通常用关键字参数更简单

>>> dict(sape=4139, guido=4127, jack=4098)

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

>>>  


4.6
循环技术

在字典中循环时,关键字和对应的值可以使用iteritems()方法同时解读出来

>>> knight = {'gallahad':'the pure','robin':'the brave'}

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

...     print k, v

...    

gallahad the pure

robin the brave

>>>

在序列中循环时,索引位置和对应值可以使用enumerate()函数同时得到。

>>> for i, v in enumerate(['a','b','c']): # 在链表中

...     print i, v

...    

0 a

1 b

2 c

>>> t = 'a', 'b', 'c'

>>> for m, n in enumerate(t): #在元组中

...     print m, n

...    

0 a

1 b

2 c

>>>

同时循环两个或更多的序列,可以使用zip()整体解读。

>>> seq1 = ['1', '2', '3']

>>> seq2 = ['a', 'b', 'c']

>>> for m, n in zip(seq1, seq2):

...     print 'L-%s,R-%s' % (m,n)

...    

L-1,R-a

L-2,R-b

L-3,R-c

>>>

需要逆向循环序列的话,先正向定位序列,然后调用reversed()函数

>>> for i in reversed(xrange(1,10,2)):

...     print i

...    

9

7

5

3

1

>>> for i in reversed(range(1,10,2)):

...     print i

...    

9

7

5

3

1

>>>

要俺排序后的顺序循环序列的话,使用sorted()函数,它不改动原序列,而是生成一个新的排好序的序列

>>> seq = ['c','a','b','e','d']

>>> for i in sorted(set(seq)):

...     print i

...    

a

b

c

d

e

>>>  


4.7
深入条件控制

whileif语句中使用的条件不仅可以使用比较,而且可以包含任意的操作。

innot in比较操作符审核值是否在一个区间之内。操作符isis not比较两个对象是否相同;这只和像链表这样的可变对象有关。所有的比较操作符具有相同的优先级,地狱所有的数值操作。

比较操作可以传递。例如a < b == c审核是否a小于b并且b等于c

比较操作可以通过逻辑操作符andor组合,比较的结果可以用not来取反。这些操作符的优先级又低于比较操作符,这些运算符中,not具有最高优先级,or优先级最低。所以A and not B or C等于(A and not B)) or C

逻辑操作符andor也称作短路操作符:它们的参数从左向右解析,一旦结果可以确定就停止。作用域一个普通的非逻辑值时,短路操作符的返回值通常是最后一个变量。

可以把比较或其它逻辑表达式的返回值赋给一个变量。

>>> string1,string2,string3 = '', 'Trondheim', 'Hammer Dance'

>>> non_null = string1 or string2 and string3

>>> non_null

'Hammer Dance'

>>> non_null = string1 or string2 or string3

>>> non_null

'Trondheim'

>>> string4 = 'hello'

>>> non_null = string1 or string2 or string3 or string4

>>> non_null

'Trondheim'

>>> non_null = string1 or string2 or string3 and string4

>>> non_null

'Trondheim'

>>> non_null = string1 and string2 or string3

>>> non_null

'Hammer Dance'

>>>

PythonC不同,在表达式内部不能赋值。所以在Python中不会出现这样的错误:想要在解析式中使用==时误用了=操作符。


4.8
不同序列类型的比较

序列对象可以与相同类型的其它对象比较。比较操作按字典序列进行:首先比较前两个元素,如果不同,就决定了比较的结果;如果相同,就比较厚两个元素,依此类推,直到所有序列都完成比较。如果两个元素本身就是同样类型的序列,就递归字典序比较。如果两个序列的所有子项都相等,就认为序列相等。如果一个序列是另一个序列的初始子序列,较短的一个序列就小于另一个。字符串的字典序按照单字符的ASCII顺序。

>>> seq1 = 1, 2, 3

>>> seq2 = 1, 2, 5

>>> print seq1 < seq2

True

>>> seq3 = [1, 2, 3]

>>> seq4 = [1, 2, 5]

>>> print seq3 < seq4

True

>>> seq5 = 1, 2, 3

>>> seq6 = 1.0, 2.0, 3.0

>>> print seq5 == seq6

True

>>> print (1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)

True

>>>

不同类型的对象比较是合法的。输出结果是确定而非任意的:类型按它们的名字排序。因此,一个链表list总是小于一个字符串string,一个字符串string总是小于一个元组等等。数值比较时会统一它们的数据类型,所以0等于0.0,等等。

你可能感兴趣的:([学]Python用户手册笔记_2_Data Structures)