其实在写这一章章名的时候我是很心虚的,因为我想起了数据结构与算法分析。在这一章作者只是讲解了python中的数据结构的使用,并没有涉及到稍微底层的东西,但还是感觉标题起的有点过大。。。
5.1关于list的详细信息
首先我们把list相关的方法全部罗列出来,并配以简单的解释:
list.append(x)这个方法会把x增加到list的末尾,相当于以前我们学到的a[len(a):] = x
list.extend(L)这个方法和append差不多,只是增加的L变成了另一个list,相当于a[len(a):] = L
list.insert(i,x)顾名思义insert就是插入了,把x插入到第i个位置,就是说插入后list[i]就是x
list.remove(x)首先在list里面搜索x,如果有就删除这个元素, 如果没有。。。那就报错了
list.pop([i])推出指定位置的元素,并且返回给调用方法的人。注意方括号表示可选元素,如果什么都不填的话默认为最后一个元素
list.clear()删光list里面的所有元素,相当于del a[:]
list.sort(key = None, reverse = False)将list内的元素排序,详情我们稍后再说
list.reverse()刚list内现在的顺序反过来
list.copy()返回一个list的复制品
我们把大部分的方法都用一用:
5.1.1使用list实现堆
使用list的方法可以很好的实现堆(一种后进先出的数据结构),入栈使用append,出栈使用pop,举个栗子:
5.1.2使用list实现队列
用list来实现队列也是很方便的,只要保证先进先出就可以了,当然了list做这个并不是很有效率。因为pop和append是在末尾操作元素,如果在前面操作的话会使后面的元素前移。在实现queue的时候我们会使用到collections这个模块中的deque类,这个类被设计的能够快速的进出队列。举个栗子:
5.1.3列表推导式
列表推导式为我们提供了一种简明的方法去创造列表。一般情况我们创造列表的目的往往是为了接收其他某些操作的结果或是特定情况下一下元素的子集,举个栗子假设我们要创建一个如下的列表:
在这个栗子中我们为了得到平方数,所以定义了x这个平方根。得到squares列表后x依旧存在。我们可以无副作用的得到平方数:
squares = list(map(lambda x : x**2, range(10)))
他等同于:
squares = [ x**2 for x in range(10)]
显然下面这个式子更清楚,表达的意思也清晰。
一个列表推导式的组成肯定是要有一对方括号的,在方括号内左边是元素的计算方法,右边是一个或多个for循环和if结构。通过对推导式内容的计算返回一个结果就是我们需要的列表,我们还可以用这个方式结合两个列表:
这个方法其实等同于:
其实这个结构没什么麻烦的,只要注意for和if的使用就可以了,如果要返回元组就必须打上括号。
列表推导式还可以使用嵌套的方程和复杂的表达式:
5.1.4嵌套的列表推导式
一个列表推导式可以是任意的表达式,也可以是另一个列表推导式。我们先做一个3X4的矩阵:
其实这个内容和上一章差不多,我们把[[row[i] for row in matrix]当成一个在for i in range(4)下循环的表达式就行了:
当然第一个for循环中的推导式也可以展开:
当然在实际生活中,我们还是应该优先选用内建的函数来完成工作,比如zip()方法就是一个很好的矩阵倒置方法:
5.2del声明
del可以直接按照序号删除元素,不用知道元素的内容是什么,与pop不同del不会返回被删除的值。del也可以分片删除或者清空整个列表:
关于del的其他用法我们稍后再讲。
5.3元组
我们可以看出list和string有许多相似的地方,比如都可索引或者分片操作。他们都是属于sequence(序列)这种数据类型的。因为python是一个不断在进化的语言,其他的数据类型时刻都可能被添加进来。现在介绍一下元组(tuple)他也属于sequence,元组使用逗号分隔多个值,同时他也是可以嵌套的:
同时要记住,元组是不能更改的:
但是元组可以包含可修改的list或者string。如你所见输出的元组都是包含在括号内的,这保证了我们嵌套元组的正确性。再输入元组的时候是否使用括号都是允许的,但通常我们还是加上的好。虽然列表和元组看上去很像但他们一般用作不同的情景和目的。元组是不可修改的通常用来包含那些被接受的不同类型的元素,列表中的元素一般都是同类型的,并且被迭代遍历的接受。在定义一个空元组或者只有一个元素的元组时有些奇怪,语法上为这种情况留下了一个怪异的特例,空元组使用一对括号来构建,只有一个元素的元组构件时在元素后面加上一个逗号,这虽然看上去很丑,但是很有效率:
t = (123, 34, 45)这种形式我们称为打包一个元组,我们把三个元素打包在了一起。我们也可以解压他,比如使用三个变量来接受元组内的三个元素:
x, y, z = t。解压元组时左侧用来接受元组内元素的变量,必须与元组内的总变量数相等!
5.4sets
python同样包含了set结构。他是一种无序的不重复的元素集,一般用来做会员资格测试或者是除重复。set类支持一些数学操作比如求交集、求并集、求差、求对称差。我们使用花括号{}或者set()函数来创建set类,需要注意空的集合不能用{}必须使用set()方法来创建。使用{}的话创建出来的是一个空字典~
和列表推导式一样,集合也有推导式使用方法也是一样的:
5.5Dictionaries
字典也是一种很常用的数据结构,在其他语言中可能叫做‘联想记忆’或者‘联想数组’,和序列使用数字作为索引是不同的,字典使用的时关键字(key),任何不可变的类型都可以作为key使用,数字和字符串也是同样允许的。我们在定义元组是强调了他不可修改,所以元组也是可以作为字典的,但是有一定的局限性元组内不能包含可变的类型。我们是不能用列表作为关键字的,因为列表可以被改变。
在理解字典的时候我们最好把关键字和元素当作一个整体,字典是一个饱和许多无序的这样的整体的集合。一对花括号可以生成一个空字典,在最初生成字典的时候每一对之间使用逗号隔开,输出字典的时候也是这种格式。字典的主要功能就是按照关键字储存和提取对应的值,也可以中del来删除字典中的一对键值。在增加新的一对键值的时候如果以前有一样的关键字,那么以前的那一堆就会被消除。如果调用一个不存在的关键字来提取变量那就会报错。
可以使用list(d.key())来返回字典中的所有关键字,但是这种方法返回的关键字是无序的,使用sorted(d.key())可以返回有序的关键字。使用in可以检查一个关键字是否存在于字典中。下面是一个简单的字典:
dict()可以直接使用序列来构造字典:
同样的字典也可以使用推导式:
5.6Looping Techniques
在循环字典的时候可以使用items()方法来同时获取关键字和他对应的值。
在循环序列的时候可以使用enumerate()函数来同时返回元素和元素位置:
可以使用zip()函数同时循环两个以上的序列:
5.7 More on Conditons
在if和while条件中,可以使用许多运算。我们用in和not in来检查一个元素是否在序列中;所有的比较操作的优先级是相同的,但是要不运算符号的优先级低。而且比较符号可以连接着使用,比如a < b == c 他会测试a是否小于b,然后判断b是否等于c。and和or会返回布尔类型的结构,和not配合会返回和原来相反的结果,他们的优先级低于比较,not是其中优先级最高的,or是最低的;举个例子a and not b or c 等价于(a and (not b)) or c。
布尔操作也被叫做短路运算符:他会从左到右进行判断,如果结果确定了就不会继续判断而是直接返回结果。举个例子,如果A和C是真的,但是B是假的。那么A and B and C并不会去判断B and C他会在A and B判断为假后直接返回False。注意在python中我们不在比较中对变量进行赋值,这点可能让C语言用户抱怨但是他很安全~
5.8 Comparing Sequenc and Other Types
序列是可以相互比较的,计较的规则和辞典的排序规则是一样的:首先先比较各自第一个元素,如果相等就比较第二个元素如不相等就可以直接返回结构。如果全部一样就算相等,字符在比较的时候是按照unicode编码序号来比较的。举几个简单的例子很好看明白:
(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)
如果比较对象的类型不一致,比如用数字和字符对边就会引起类型异常。