5.数据类型
5.1关于列表更多的内容
Python的列表数据类型包含更多的方法。这里是所有的列表对象方法:
-list.append(x) 把一个元素添加到列表的结尾,相当于 a[len(a):]=[x]
-list.extend(L) 将一个给定列表中的所有元素都添加到另一个列表中,相当于a[len(a):]=L
-list.insert(i,x) 在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如a.insert(0,x)会插入到整个列表之前,而a.insert(len(a),x)相当于a.append()
-list.remove(x) 删除列表中值为x的第一个元素。如果没有这样的元素,就会返回一个错误
-list.pop([i]) 从列表的指定位置删除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素
-list.clear() 从列表中删除所有元素。相当于 del a[:]
-list.index(x)返回列表中第一个值为x的元素的索引。如果没有匹配的元素就会返回一个错误
-list.count(x) 返回x在列表中出现的次数
-list.sort() 对列表中的元素就地进行排序
-list.reverse() 就地倒排列表中的元素
-list.copy() 返回列表的一个浅拷贝。等同于a[:] 深浅拷贝在我博客中讲到:
numbers=[12,1.32,5,76,123,34,32,34]
print(numbers.count(34),numbers.count(1.32),numbers.count('x'))
2 1 0
numbers.insert(2,-1)
numbers.append(333)
a
987
numbers
[12, 1.32, -1, 5, 76, 123, 34, 32, 34, 333]
numbers.index(333)
9
numbers.remove(333)
numbers
[12, 1.32, -1, 5, 76, 123, 34, 32, 34]
numbers.reverse()
numbers
[34, 32, 34, 123, 76, 5, -1, 1.32, 12]
numbers.sort()
numbers
[-1, 1.32, 5, 12, 32, 34, 34, 76, 123]
numbers.pop()
123
numbers
[-1, 1.32, 5, 12, 32, 34, 34, 76]
也许大家会发现像insert,remove或者sort这些修改列表的方法没有打印返回值--它们返回None。在python中对所有可变的数据类型这是统一的设计原则。
5.1.1把列表当作堆栈使用
列表方法使得列表可以很方便地作为一个堆栈来使用,堆栈作为特定的数据结构,最先进入的元素最后被释放(后进先出)。用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
[3, 4, 5]
stack.pop()
5
5.1.2把列表当作队列使用
你也可以把列表当作队列使用,队列作为特定的数据结构,最先进入的元素最先释放(先进先出)。不过,列表这样用效率不高。相对来说从列表末尾添加和弹出很快;在头部插入和弹出很慢(因为,为了一个元素,要移动整个列表中的所有元素)。要实现队列,使用collections.deque,它为在首尾两端快速插入和删除而设计。例如:
5.1.3列表推导式
列表推导式为从序列中创建列表提供了一个简单的方法。普通的应用程序通过将一些操作应用于序列的每个成员并通过返回的元素创建列表,或者通过满足特定条件的元素创建子序列。例如假设我们创建一个squares列表,可以像下面方式:
注意这个for循环中的被创建(或被重写)的名为x的变量在循环完毕后依然存在。使用如下方法,我们可以计算squares的值而不会产生任何的副作用。
或者等价于:
上面这个方法更加简明且易读
列表推导式由包含一个表达式的括号组成,表达式后面跟随一个for子句,之后可以有零或多个for或if子句。结果是一个列表,由表达式依据其后面的for和if子句上下文计算而来的结果构成。
例如,如下的列表推导式结合两个列表的元素,如果元素之间不相等的话:
等同于:
值得注意的是在上面两个方法中的for和if语句的顺序
列表推导式可使用复杂的表达式和嵌套函数:
5.1.4嵌套的列表推导式
列表解析中的第一个表达式可以是任何表达式,包括列表解析。
考虑下面有三个长度为4的列表组成的3*4矩阵:
现在,如果你想交换行和列,可以用嵌套的列表推导式:
像前面看到的,嵌套的列表推导式是对for后面的内容进行求值,所以上例就等价于:
反过来说,如下也是一样的:
在实际中,你应该更喜欢使用内置函数组成复杂流程语句。对此种情况zip()函数将会做的更好:
更多关于本行中使用的星号的说明,参考 参数列表的分拆
5.2del语句
有个方法可以从列表中按给定的索引而不是值来删除一个子项:del语句。他不同于有返回值的pop()方法。语句del还可以从列表中删除切片或清空整个列表(我们以前介绍一个方法是将空列表赋值给列表的切片)。例如:
del也可以删除整个变量:
此后在引用命名a会引发错误(直到另一个值赋给它为止)。我们在后面的内容中可以看到del的其他用法。
5.3元组和序列
我们知道列表和字符串有很多通用的属性,例如索引和切割操作。它们是序列类型(参见 Sequence Types--list,tuple,range)中的两种。因为python是一个在不停进化的语言,也可能会加入其它的序列类型,这里介绍另一种标准序列类型:元组
一个元组由数个逗号分隔的值组成,元组的体征是逗号。例如:
如你所见,元组在输出是总是有括号的,以便正确表达嵌套结构。在输出是可以有或没有括号,不过经常括号都是必须的(如果元组是一个更大的表达式的一部分)。不能给元组的一格独立的元素赋值(尽管你可以通过联接和切割来模拟)。还可以创建包含可变对象的元组,例如列表。
虽然元组和列表很类似,它们经常被常用来在不同的情况和不同的用途。元组有很多用途,例如(x,y)坐标对,数据库中的员工记录等等。元祖就像字符串,不可变的。通常包含不同种类的元组并通过分拆(参阅本节后面的内容)或索引访问(如果是namedtuples,甚至可以通过属性)。列表是可变的。它们的元素通常是相同类型的并通过迭代访问。
一个特殊的问题是构造包含零个或一个元素的元组:为了适应这种情况,语法上有一些额外的改变。一对空的括号可以创建空元组;要创建一个单元素可以在值后面跟一个逗号(在括号中放入一个单值不够明确)。不好但是有效。例如:
语句tuple1=123,456,’lq6h’是元组封装(tuple packing)的一个例子:值123,456和’lq6h’被封装进元组。其逆操作可能是这样:
这个调用等号右边可以是任何线性序列,称之为序列拆封非常恰当。序列拆封要求左侧的变量数目与序列的元素个数相同。要注意的是可变参数(multiple assignment)其实只是元组封装和序列拆封的一个组合。
5.4集合
Python还包含了一个数据类型--set(集合)。集合是一个无序不重复元素的集。基本功能包括测试和消除重复元素。集合对象还支持union(联合),(intersection)交,differernce(差)和sysmmetric difference(对称差集)等数学运算。
大括号或set()函数可以用来创建集合。注意:想要创建空集合,你必须使用set()而不是{ }。后者用于创建空字典,我们在下一节介绍的一种数据结构。
以下是一个简单的演示:
name1=set('HenL')
name2=set('LQ6H')
name1
{'H', 'L', 'n', 'e'}
name1-name2
{'n', 'e'}
name1 | name2
{'Q', '6', 'e', 'L', 'n', 'H'}
name1&name2
{'H', 'L'}
name1^name2
{'Q', '6', 'e', 'n'}
-name1-name2 #letters in name1 but not in name2
-name1 | name2 #letters in either name1 or name2
-name1&name2 #letters in both name1 and name2
-name1^name2 #letters in name1 or name2 but not both
类似列表推导式,这里有一种集合推导式语法:
5.5字典
另一个非常有用的python内建数据类型是字典(参见Mapping Types--dict)。字典在某些语言中可能称为联合内存(associative memories)或联合数组(associative arrays)。序列是以连续的整数为索引,关键字可以是任意不可变类型,通常用字符串或数值。如果元组中只包含字符串和数字,它可以作为关键字,如果它直接或间接的包含了可变对象,就不能当作关键字。不能用列表做关键字,因为列表可以用索引,切割或者append()和extend()等方法改变。
理解字典的最佳方式是把它看做无序的键值对(key:value对)集合,键必须是互不相同的(在一个字典之内)。一对大括号创建一个空的字典:{ }。初始化列表时,在大括号内放置一组逗号分隔的键,值对。这也是字典输出的方式。
字典的主要操作是依据键来存储和析取值。也可以用del来删除键值对(key:value)。如果你用一个已经存在的关键字存储值,以前为该关键字分配的值就会被遗忘。试图从一个不存在的键中取值会导致错误。
对一个字典执行list(d.keys)将返回一个字典中所有关键字组成的无序列表(如果你想排序,只需使用sorted(d.keys()))。使用in关键字(指python语法)可以检查字典中是否存在某个关键字(指字典)。
这里是使用字典的一个小示例:
-dict()构造函数可以直接从key-value对中创建字典:
此外字典推导式可以从任意的键值表达式中创建字典:
如果关键字都是简单的字符串,有时通过关键字参数指定key-value对更为方便:
5.6循环技巧
在字典中循环时,关键字和对应的值可以使用items() 方法同时解读出来
在序列中循环时,索引位置和对应值可以使用enumerate()函数同时得到:
同时循环两个或更多的序列,可以使用zip()整体打包
需要逆向循环序列的话,先正向定位序列,然后调用reversed()函数:
要按排序后的顺序循环序列的话,使用sorted()函数,它不改动原序列,而是生成一个新的已排序的序列:
若要在循环内部修改正在遍历的序列(例如复制某些元素),建议你首先制作副本。在序列上循环不会隐式地创建副本。切片表示法是这尤其方便:
5.7深入条件控制
-while和if语句中使用的条件不仅可以使用比较,而且可以包含任意的操作。
比较操作符in和not in审核值是否在一个区间之内。操作符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。作用于一个普通的非逻辑值时,短路操作符的返回值通常是最后一个变量。
可以把比较或其他逻辑表达式的返回值赋给一个变量,例如:
需要注意的是python与C不同,在表达式内部不能赋值。C程序员经常抱怨,不过他避免一类在C程序中司空见惯的错误:想要在解析式中使==时误用了=操作符。
5.8比较操作符和其他类型
序列对象可以与相同类型的其他对象比较。比较操作按字典序进行:首先比较前面1两个元素,如果不同,就决定了比较结果;如果相同,就比较后两个元素,以此类推。直到所有序列都完成比较。如果两个元素本本身就是同样类型的序列。就递归字典序比较。如果两个序列的所有子项都相等,就认为序列相等。如果一个序列是另一个序列的初始子序列,较短的一个序列就小于另一个。字符串的字典序按照单字符的ASCII顺序。下面是同类型序列之间比较的一些例子:
需要注意的是如果通过<或者>比较的对象只要具有合适的比较方法就是合法的,比如,混合数值类型是通过它们的数值进行比较的,所以0是等于0.0。否则解释器将会触发一个TypeError异常,而不是提供一个随意的结果。
补充:
别的语言可能返回一个变化的对象,允许方法连续执行,像 d->insert(‘a’)->remove(‘b’)->sort();
调用d.keys()将会返回一个dictionary view 对象。它支持成员测试以及迭代等操作,但是它的内容不是独立的原始字典--它只是一个视图。