个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。
目录
列表和字典,这两个对象类型都是其他对象的集合。这两种类型几乎是Python所有脚本的主要工作组件。这两种类型都相当灵活:它们都可以在原处进行修改,也可以按需求增长或缩短,而且可以包含任何种类的对象或者被嵌套。借助这些类型,可以在脚本中创建并处理任意的复杂的信息结构。
列表
列表是Python中最具灵活性的有序集合对象类型。
与字符串不同的是,列表可以包含任何种类的对象:数字、字符串甚至其他列表。列表都是可变对象,它们都支持在原处修改的操作,可以通过指定的偏移值和分片、列表方法调用、删除语句等方法来实现。
Python列表是:
任意对象的有序集合
列表就是收集其他对象的地方,你可以把它们看做组。同时列表所包含的每一项都保持了从左到右的位置顺序(也就是说,它们是序列)。
通过偏移读取
就像字符串一样,你可以通过列表对象的偏移对其进行索引,从而读取对象的某一部分内容。由于列表的每一项都是有序的,那么你也可以执行诸如分片和合并之类的任务。
可变长度、异构以及任意嵌套
列表可以实地的增长或者缩短(长度可变),并且可以包含任何类型的对象而不仅仅是包含有单个字符的字符串(异构)。列表能够包含其他复杂的对象,又能够支持任意的嵌套,所以可以创建列表的子列表的子列表等。
属于可变序列的分类
列表支持在原处的修改(它们是可变的),也可以响应所有针对字符串序列的操作,例如,索引、分片以及合并。列表是可变的,因此它们也支持字符串不支持的其他操作(例如,删除和索引赋值操作,它们都是在原处修改列表)。
序列操作在列表与字符串中的工作方式相同。唯一的区别是:当应用于字符串上的合并和分片这样的操作应用于列表时,返回新的列表。
对象引用数组
Python列表包含了零个或多个其他对象的引用。从Python的列表中读取一个项的速度与索引一个C语言数组差不多。在标准Python解释器内部,列表就是C数组而不是链接结构。
每当用到引用时,Python总是会将这个引用指向一个对象,所以程序只需处理对象的操作。当把一个对象赋给一个数据结构元素或变量名时,Python总是会存储对象的引用,而不是对象的一个拷贝(除非明确要求保存拷贝)。
列表对象操作
总结了常见的和具有代表性的列表对象操作。
为了得到更全面的信息,可以查阅Python的标准库手册,或者运行help(list)或dir(list)查看list方法的完整列表清单。
当作为常量表达式编写时,列表会被写成系列对象(实际上是返回对象的表达式),这些对象括在方括号中并用逗号隔开。
例如,第一个是将变量L赋给一个四项的列表。第二个是嵌套的列表写成一串嵌套的方括号。第三个空列表就是一对内部为空的方括号。
>>> l = [0,1,2,3]
>>> l = ['a',[0,1,'aa']]
>>> l = []
大多数操作看上去应该很熟悉,因为它们都与我们先前在字符串上使用的序列操作相同,例如,索引、合并和迭代等。列表除了支持在原处的修改操作(删除项、赋值给索引和分片等)之外,还可以进行特定的列表方法调用(它们可完成排序、反转操作以及在结尾添加元素等任务),列表可以使用这些工具来进行修改操作是因为列表是可变的对象类型。
注意:在列表处理程序中,不会看到有很多列表写成这样。比较常见的代码是处理动态建立的列表(运行时)。实际上,虽然精通常量语法也很重要,但Python中多数数据结构的建立都是在运行时执行程序代码的。
基本列表操作
由于列表是序列,它支持很多与字符串相同的操作。例如,列表对“+”和“*”操作的响应与字符串很相似,两个操作的意思也是合并和重复,只不过结果是一个新的列表,而不是一个字符串:
>>> len([1,2,3])
3
>>> [1,2,3] + [4,5,6]
[1, 2, 3, 4, 5, 6]
>>> ['hi'] * 4
['hi', 'hi', 'hi', 'hi']
尽管列表的“+”操作和字符串中的一样,然而值得重视的是“+”两边必须是相同类型的序列,否则运行时会出现类型错误。例如,不能将一个列表和一个字符串合并到一起,除非你先把列表转换为字符串(使用诸如反引号、str或者%格式这样的工具),或者把字符串转换为列表(列表内置函数能完成这一转换):
>>> str(['a']) + 'b'
"['a']b"
>>> ['a'] + list('b')
['a', 'b']
列表迭代和解析
列表对字符串使用的所有序列操作都能做出响应,包括迭代工具:
>>> 3 in [1,3,4]
True
>>> for x in [1,3,4]:
... print(x)
...
1
3
4
for循环从左到右地遍历任何序列中的项,对每一项执行一条或多条语句。
表8-1中的最后一项,列表解析和map调用,在后面会有更详细的开展介绍。它们的基本操作是很简单的,列表解析只不过是通过对序列中的每一项应用一个表达式来构建一个新的列表的方式,它与for循环密切相关:
>>> res = [c * 4 for c in 'life']
>>> res
['llll', 'iiii', 'ffff', 'eeee']
这个表达式功能上等同于手动构建一个结果的列表的一个for循环,列表解析的编码更简单,而且如今运行起来更快:
>>> res = []
>>> for x in 'life':
... res.append(x * 4)
...
>>> res
['llll', 'iiii', 'ffff', 'eeee']
内置函数map做类似的工作,但它对序列中的各项应用一个函数并把结果收集到一个新的列表中:
>>> list(map(abs,[-1,0,1,2,3]))
[1, 0, 1, 2, 3]
后面会详细介绍迭代解析表达式
索引、分片和矩阵
列表都是序列,对于列表而言,索引和分片操作与字符串中的操作基本相同。然而对列表进行索引的结果就是你指定的偏移处的对象(不管是什么类型),而对列表进行分片时往往返回一个新的列表:
>>> l = ['he','life','aa']
>>> l[1]
'life'
>>> l[-1]
'aa'
>>> l[1:]
['life', 'aa']
注意:由于可以在列表中嵌套列表(和其他对象类型),有时需要将几次索引操作连在一起使用来深入到数据结构中去。
举个例子,最简单的办法之一是将其表示为矩阵(多维数组),在Python中相当于嵌套了子列表的列表。在这里我们看一个基于列表的3×3的二维数组:
>>> matrix = [[1,2,3],[4,5,6],[7,8,9]]
>>> matrix[1]
[4, 5, 6]
>>> matrix[1][1]
5
>>> matrix[2][0]
7
就高性能数值运算的工作来说,NumPy扩展提供了处理矩阵的其他方式。
原处修改列表
列表是可变的,支持原处改变列表对象的操作。也就是说,下面的操作都可以直接修改列表对象,而不会像字符串那样强迫建立一个新的拷贝。
Python只处理对象引用,所以需要将原处修改一个对象与生成一个新对象区分开来,如果你在原处修改一个对象时,可能同时会影响一个以上指向它的引用。
索引与分片的赋值
当使用列表的时候,可以将它赋值给一个特定项(偏移)或整个片段(分片)来改变它的内容:
>>> l = ['he','life','aa']
>>> l[1] = 'bb'
>>> l
['he', 'bb', 'aa']
>>> l[0:2] = ['hit','cc']
>>> l
['hit', 'cc', 'aa']
索引和分片的赋值都是原地修改,它们对列表进行直接修改,而不是生成一个新的列表作为结果。
最后一个操作是分片赋值,它仅仅用一步操作就能够将列表的整个片段替换掉。
分片赋值最好分成两步来理解:
1.删除。删除等号左边指定的分片。
2.插入。将包含在等号右边对象中的片段插入旧分片被删除的位置
当赋值的值与分片重叠时,就需要详细的分析:例如,L[2:4]=L[3:5]是可行的,这是因为要被插入的值会在左侧删除发生前被取出
>>> l[2:] = ['z','x','c','v']
>>> l
['hit', 'cc', 'z', 'x', 'c', 'v']
>>> l[2:4] = l[3:5]
>>> l
['hit', 'cc', 'x', 'c', 'c', 'v']
理解为什么插入元素的数目不需要与删除的数目相匹配。比如上面的 l[2:] 。
再比如,已知一个列表L的值为[1,2,3],赋值操作L[1:2]=[4,5]会把L修改成列表[1,4,5,3]。Python会先删除2(单项分片),然后在删除2的地方插入4和5。这也解释了为什么L[1:2]=[]实际上是删除操作——Python删除分片(位于偏移为1的项)之后什么也不插入。
实际上,分片赋值是一次性替换整个片段或“栏”。因为被赋值的序列长度不一定要与被赋值的分片的长度相匹配,所以分片赋值能够用来替换(覆盖)、增长(插入)、缩短(删除)主列表。
但是分片也是在实践当中不常见的操作。Python通常还有更简单直接的方式实现替换、插入以及删除(例如,合并、insert、pop以及remove列表方法),实际上那些才是Python程序员比较喜欢用的。
列表方法调用
Python列表对象也支持特定类型方法调用,其中很多调用可以在原处修改主体列表:
>>> l
['hit', 'cc', 'x', 'c', 'c', 'v']
>>> l.append('aa') #新增
>>> l
['hit', 'cc', 'x', 'c', 'c', 'v', 'aa']
>>> l.sort() #对列表排序'S'<'s'
>>> l
['aa', 'c', 'c', 'cc', 'hit', 'v', 'x']
方法就是附属于特定对象的函数(实际上是引用函数的属性)。方法提供特定类型的工具。例如,我们这里介绍的列表方法只适用于列表。
最常用的列表方法是append,它能够简单地将一个单项(对象引用)加至列表末端。与合并不同的是,append允许传入单一对象而不是列表。L.append(X)与L+[X]的结果类似,不同的是,前者会原地修改L,而后者会生成新的列表。
L[len(L):]=[X]就与L.append(X)一样,而L[:0]=[X]就好像在列表前端附加那样。
>>> l = ['aa', 'c', 'c', 'cc', 'hit', 'v', 'x']
>>> l[len(l):] = [22]
>>> l
['aa', 'c', 'c', 'cc', 'hit', 'v', 'x', 22]
>>> l = ['aa', 'c', 'c', 'cc', 'hit', 'v', 'x']
>>> l.append(22)
>>> l
['aa', 'c', 'c', 'cc', 'hit', 'v', 'x', 22]
另一个常见方法是sort,它原地对列表进行排序。sort是使用Python标准的比较检验作为默认值(在这里指字符串比较),而且以递增的顺序进行排序。
可以通过传入关键字参数来修改排序行为——这是指定按名称传递的函数调用中特殊的"name=value"语法,常常用来给定配置选项。在排序中,key主要是用来进行比较的元素,只有一个参数指定可迭代对象中的一个元素来进行排序,reverse参数允许排序按照降序而不是升序进行:
>>> l = ['abc','ABC','aBc']
>>> l.sort()
>>> l
['ABC', 'aBc', 'abc']
>>> l.sort(reverse = False) #升序(默认)
>>> l
['ABC', 'aBc', 'abc']
>>> l.sort(reverse = True) #降序
>>> l
['abc', 'aBc', 'ABC']
与字符串相同,列表有其他方法可执行其他特定的操作。
例如,reverse可原地反转列表,extend和pop方法分别能够在末端插入多个元素、删除一个元素。也有一个reversed内置函数,像sorted一样地工作,但是,它必须包装在一个list调用中,因为它是一个迭代器(后面更详细地讨论迭代器):
>>> l = [1,2]
>>> l.extend([3,4,5]) #结尾添加多项
>>> l
[1, 2, 3, 4, 5]
>>> l.pop() #删除并返回最后一项
5
>>> l
[1, 2, 3, 4]
>>> l.reverse() #反转
>>> l
[4, 3, 2, 1]
>>> list(reversed(l)) #内置反转函数,并返回迭代对象
[1, 2, 3, 4]
在某些类型的应用程序中,往往会把这里用到的列表pop方法和append方法联用,来实现快速的后进先出(LIFO,last-in-first-out)堆栈结构。列表的末端作为堆栈的顶端:
>>> l = []
>>> l.append(1)
>>> l.append(2)
>>> l
[1, 2]
>>> l.pop()
2
>>> l
[1]
pop方法也能够接受某一个即将删除并返回的元素的偏移(默认值为最后一个元素),这一偏移是可选的。其他列表方法可以通过值删除(remove)某元素,在偏移处插入(insert)某元素,查找某元素的偏移(index)等:
>>> l = ['a','b','c']
>>> l.index('c')
2
>>> l.insert(1,'z')
>>> l
['a', 'z', 'b', 'c']
>>> l.remove('z')
>>> l
['a', 'b', 'c']
>>> l.pop(1)
'b'
>>> l
['a', 'c']
其他常见列表操作
可以用del语句在原处删除某项或某片段:
>>> l = ['a','b','c','d']
>>> l
['a', 'b', 'c', 'd']
>>> del l[0]
>>> l
['b', 'c', 'd']
>>> del l[1:] # 等于l[1:] = []
>>> l
['b']
>>> l = ['b','c','d']
>>> l[1:] = []
>>> l
['b']
分片赋值是删除外加插入操作,也可以通过将空列表赋值给分片来删除列表片段(L[i:j]=[])Python会删除左侧的分片,然后什么也不插入。
将空列表赋值给一个索引只会在指定的位置存储空列表的引用,而不是删除:
>>> l[0] = []
>>> l
[[]]
还有其他列表方法和操作并没有在这里列出(包括插入和搜索方法)。为了得到更全面的最新类型工具清单,你应该时常参考Python手册、Python的dir和help函数
讨论的原处修改操作都只适用于可变对象:无论如何,都是不能用在字符串、元组上。