1 列表
Python列表是:
- 任意对象的有序集合:从功能上看,列表就是收集其他对象的地方,你可以把它们看作组。同时列表维护了其中每一项从左到右的位置顺序(也就是说,它们是序列)
- 通过偏移访问:就像字符串一样,你可以通过列表对象的偏移对其进行索引,从而读取对象的某一部分内容。由于列表的每一项都是有序的,你也可以执行诸如分片和拼接之类的任务。
- 可变长度、异构以及任意嵌套:与字符串不同的是,列表可以原位置增长或者缩短(长度可变),并且可以包含任何类型的对象,而不仅仅是包含单个字符的字符串(列表和字符串是异构的)。因为列表能够包含其他复杂的对象,又能够支持任意的嵌套,因此你可以创建列表的子列表的子列表等。
下表总结了常见的和具有代表性的列表对象操作:
操作 | 解释 |
---|---|
L=[] | 一个空列表 |
l=[123,‘abc’,1.23,{}] | 包含四个元素的列表,索引从0到3 |
L=[‘Bob’,40.0,[‘dev’,‘mgr’]] | 嵌套的子列表 |
L=list(‘spam’) | 一个可迭代对象元素的列表 |
L=list(range(-4,4)) | 连续整数的列表 |
L[i] | 索引 |
l[i][j] | 索引的索引 |
L[i:j] | 分片 |
len(L) | 求长度 |
L1+L2 | 拼接 |
L*3 | 重复 |
for x in L: print(x) | 迭代 |
3 in L | 成员关系 |
L.append(4) | 尾部添加 |
L.extend([5,6,7]) | 尾部拓展 |
L.insert(i,x) | 插入 |
L.index(x) | 索引 |
L.count(x) | 统计元素出现次数 |
L.sort() | 排序 |
L.reverse() | 反转 |
L.copy() | 复制 |
L.clear() | 清除 |
L.pop(i) | 删除i处元素,并将其返回 |
L.remove(x) | 删除元素X |
del L[i] | 删除i处元素 |
del L[i:j] | 删除i到j处的片段 |
L[i:j]=[] | 删除i到j处的片段 |
L[i]=3 | 索引赋值 |
L[i:j]=[4,5,6] | 分片赋值 |
L=[x**2 for x in range(5)] | 列表推导和映射(第4、14、20章) |
2 列表的实际应用
2.1 基本列表操作
列表的基本操作包含+
、*
、len()
:
print(len([1,2,3])
# 3
print([1,2,3]+[4,5,6])
# [1,2,3,4,5,6]
print(['NI!']*4)
# ['NI!','NI!','NI!','NI!']
注意列表的+
两边必须是相同类型的序列,否则运行时会出现类型错误。例如我们不能将一个列表和一个字符串拼接到一起,除非我们先把列表转换成了字符串。
2.2 列表迭代和推导
print(3 in [1,2,3])
# True
for x in [1,2,3]:
print(x,end=' ')
# 1 2 3
列表推导是通过对序列中的每一项应用一个表达式来构建一个新的序列,它与for循环密切相关:
res=[c*4 for c in 'SPAM']
print(res)
# ['SSSS','PPPP','AAAA','MMMM']
内置函数map能实现类似的效果,但它对序列中的各项应用一个函数并把结果收集到一个新的列表中:
print(list(map(abs,[-1,-2,0,1,2]))
# [1,2,0,1,2]
2.3 索引、分片和矩阵
对列表进行索引的结果就是我们指定的偏移处的对象(不管是什么类型),而对列表进行分片时往往返回一个新的列表:
L=['spam','Spam','SPAM!']
print(L[2])
# 索引 'SPAM!'
print(L[-2])
# 负索引 'Spam'
print(L[1:])
# 正索引切片 ['Spam','SPAM!']
print(L[-2:])
# 负索引切片 ['Spam', 'SPAM!']
print(L[2:0:-1])
# 倒序切片 ['SPAM!', 'Spam']
print(L[::2])
# 按特定步长切片 ['spam', 'SPAM!']
2.4 原位置修改列表
由于列表是可变的,它们支持原位置改变列表对象的操作。因为Python只处理对象引用,所以我们需要着重区分原位置修改一个对象以及生成一个新对象区的不同,如果我们在原位置修改了一个对象,那么它可能同时会影响一个以上指向它的应用。
2.5 索引与分片的赋值
当使用列表时,可以将它赋值给一个特定项(偏移)或整个片段(分片)来改变它的内容:
L=['spam','Spam','SPAM!']
L[1]='eggs'
print(L)
# ['Spam','eggs','SPAM!']
L[0:2]=['eat','more']
print(L)
# ['eat','more','SPAM!']
索引和分片的赋值都是原位置修改,它们对主体列表进行直接修改,而不是生成一个新的列表作为结果。
注意分片赋值时插入元素的数目不需要与被删除的数目相匹配。以下面的例子为例,Python将[1:1]之间的空白切片替换为两个元素,同时可以用分片赋值进行删除:
L=[1,2,3]
L[1:2]=[4,5]
print(L)
# [1,4,5,3]
L[1:1]=[6,7]
print(L)
# [1,6,7,4,5,3]
L[1:2]=[]
print(L)
# [1,7,4,5,3]
由于被赋值的序列长度不一定要与被赋值的分片的长度相匹配,所以分片赋值能够用来替换(覆盖)、增长(插入)、缩短(删除)主体列表。我们可以在头尾进行操作(在尾部进行的操作和exten函数等价):
L=[1]
L[:0]=[2,3,4]
print(L)
# [2,3,4,1]
L[len(L):]=[5,6,7]
print(L)
# [2,3,4,1,5,6,7]
L.extend([8,9,10])
print(L)
# [2,3,4,1,5,6,7,8,9,10]
2.6 列表方法调用
Python列表对象支持特定类型方法调用,其中很多调用可以在原位置修改主体列表:
L=['eat','more','SPAM!']
L.append('please')
print(L)
# ['eat','more','SPAM!','please']
L.sort()
print(L)
# ['SPAM!','eat','more','please']
最常用的列表方法是append,它能够简单地将一个单项(对象引用)加至列表末端。与拼接不同的是,append允许传入单一对象而不是列表。L.append(X)与L+[X]的结果类似,不同的是,前者会原位置修改L,而后者会生成新的列表。
2.7 更多关于列表排序
sort原位置对列表进行排序,其使用了Python标准的比较检验作为默认检验,而且默认采用递增的顺序进行排序。我们可以通过参数对sort的行为进行修改。
在排序函数中,reverse参数允许排序以降序而不是升序运行。我们还可以通过key
参数指定排序规则:
test=[[1,2],[2,3],[3,4],[1,3]]
test.sort()
print(test)
# 输出结果以list首个元素进行排序
# [[1, 2], [1, 3], [2, 3], [3, 4]]
def helper(elem):
return elem[-1]
intervals.sort(key=helper)
print(intervals)
# 此时输出结果以list最后一个元素进行排序
# [[1, 2], [1, 3], [2, 3], [3, 4]]
注意:虽然append和sort都是原位置进行修改,但是二者的结果并没有返回列表(返回值的为None)。如果我们编写类似于L=L.append(X)
的语句,不会得到L修改后的值。
2.8 其他常见列表方法
L=[]
L.append(1)
L.append(2)
print(L)
# [1,2]
print(L.pop())
# 2
print(L)
[1]
pop方法也能够接受某一个即将被删除并返回的元素的偏移量(默认值为最后一个元素的偏移量,也就是-1)。其他列表方法包括通过值删除(remove)某元素,在偏移处插入(insert)某元素,计算某元素的出现次数(count),查找某元素的偏移(index)等:
L=['spam','eggs','ham']
print(L.index('eggs'))
# 1
L.insert(1,'toast')
print(L)
# ['spam','toast','eggs','ham']
L.remove('eggs')
print(L)
# ['spam','toast','ham']
print(L.pop(1))
# 'toast'
print(L)
# ['spam','ham']
print(L.count('spam'))
# 1
注意,与其他列表方法不同,count和index不会改变到列表本身,不过能返回列表内容相关的信息。
2.9 其他常见列表操作
由于列表是可变的,我们可以使用del语句在原位置删除某项或某片段:
L=['spam','eggs','ham','toast']
del L[0]
print(L)
# ['eggs','ham','toast']
del L[1:]
print(L)
# ['eggs']
我们可以将分片赋值认为是删除外加插入操作,因此可以通过将空列表赋值给分片来删除列表片段(L[i:j]=[])。Python会删除左侧的片,然后什么也不插入。另一方面,如果将空列表赋值给一个索引,则会在指定的位置存储空列表对象的引用,而不是删除该位置上的元素:
L=['Already','got','one']
L[1:]=[]
print(L)
# ['Already']
L[0]=[]
print(L)
# [[]]
3 字典
Python字典的主要属性如下:
- 通过键而不是偏移量来读取:字典有时又叫做关联数组(associative array)或散列表(hash,尤其是被其他脚本语言的用户这么称呼)。它们通过键将一系列值联系起来,这样我们就可以使用键从字典中取出初始存储于该键下的一项。像列表一样,我们可以使用索引操作从字典中获取内容。但是索引采用键的形式,而不是相对偏移。
- 任意对象的无序集合:与列表不同,保存在字典中的项并没有特定的顺序;实际上,Python 将各项伪随机地从左到右随机排序,以便快速查找。键提供了字典中项的象征性(而非物理性的)位置。
- 长度可变、异构、任意嵌套:与列表相似,字典可以在原位置增长或缩短(无需另外生成一份副本)。它们可以包含任何类型的对象,而且它们支持任意深度的嵌套(可以包含列表和其他字典等)。每个键只有一个与之相关联的值,但是这个值可以是一系列多个所需对象的集合,而一个值也可以同时存储在多个键下。
- 属于“可变映射”类型:通过给索引赋值,字典可以在原位置修改(字典是可变的),但不支持用于字符串和列表中的序列操作。实际上,因为字典是无序集合,所以根据固定顺序进行操作(如拼接和分片操作)是行不通的。相反,字典是唯一内置的、核心的映射类型一也就是把键映射到值的对象。Python 中其他的映射需要通过导入模块来创建。
- 对象引用表( 散列表):如果说列表是支持位置读取的对象引用数组,那么字典就是支持键读取的无序对象引用表。从本质上讲,字典是作为散列表(支持快速检索的数据结构)来实现的,一开始很小,并根据要求而增长。此外,Python 采用优化过的散列算法来寻找键,因此搜索是很快速的。和列表一样,字典存储的是对象引用(不是副本,除非你显式地要求它们这样做)。
字典的类型名是dict,表8-2总结了一些最为普通且具有代表性的字典操作:
操作 | 解释 |
---|---|
D={} | 空字典 |
D={‘name’:‘Bob’, 'age‘:40} | 有两个元素的字典 |
E={‘cto’:{‘name’:'Bob",‘age’:40}} | 嵌套 |
D=dict(name='Bob’,age=40) | 其他构造方法:关键字 |
D=dict([(‘name’,‘Bob’),(‘age’,40)]) | 通过list创建(键值对) |
D=dict(zip(keyslist,valslist)) | dict和zip组合创建 |
D=dict.fromkeys([‘a’,‘b’]) | 键列表 |
D[‘name’] | 通过键索引 |
E[‘cto’][‘age’] | 嵌套索引 |
‘age’ in D | 成员关系:键存在测试 |
D.keys() | 方法:所有键 |
D.values() | 所有值 |
D.items() | 所有“键+值”元组 |
D.copy() | 复制(顶层的) |
D.clear() | 清除(删除所有项目) |
D.update(D2) | 通过键合并 |
D.get(key,default?) | 通过键获取,如果不存在默认返回None |
D.pop(key,default?) | 通过键删除,如果不存在返回错误 |
D.setdefault(key,default?) | 通过键获取,如果不存在默认设置为None |
D.popitem() | 删除/返回所有的(键、值)对 |
len(D) | 长度(存储的键值对的对数) |
D[key]=42 | 新增/修改键、删除键 |
del D [key] | 根据键删除条目 |
list(D.keys()) | 查看字典的键 |
D={x:x*2 for x in range(10)} | 字典推导 |
4 字典的实际应用
常见字典字面量和操作:
4.1 字典的基本操作
通常情况下,创建字典并且通过键来存储、访问其中的某项:
D={'spam':2,'ham':1,'eggs':3}
print(D['spam'])
# 2
print(D)
# {'eggs': 3, 'ham': 1, 'spam': 2}
dic=dict(zip('abc',[1,2,3]))
print(dic)
# {'a':1, 'b':2, 'c':3}
注意,字典内部键由左至右的次序几乎总是和原先输入的顺序不同。这样设计的目的是为了快速执行键查找(也就是散列查找),键会在内存中重新排序。
内置len
函数可用于字典,它能够返回存储在字典里的元素的数目。字典的in
成员关系运算符提供了键存在与否的测试方法,keys
方法则能返回字典中所有的键。
D={'spam':2,'ham':1,'eggs':3}
print(len(D))
# 3
print('ham' in D)
# True
print(list(D.keys())
# ['eggs','spam','ham']
注意第三次调用时D.keys()
需要放在一个list调用中,因为keys
返回的是一个可迭代对象,而不是一个物理的列表。
4.2 原位置修改字典
与列表相同,字典也是可变的,因此我们可以在原位置上对它们进行修改、增大以及缩短,而不需要生成新字典。只需给一个键赋值就可以改变或者生成元素。del
语句在这里也适用;它用于删除作为索引的键相关联的元素。
与列表相同,向字典中已存在的键索引赋值会改变与索引相关联的值。然而,与列表不同的是,每当对新字典键进行赋值(之前没有被赋值的键),就会在字典内生成一个新的元素。
4.3 其他字典方法
字典方法提供了多种类型特定的工具。例如,字典values和items方法分别返回字典中所有的值列表和(key,value)
对元组。
D={'spam':2,'ham':1,'egg':3}
print(list(D.values())
# [3,2,1]
print(list(D.items())
# [('eggs',3),('spam',2),('ham',1)]
我们还可以通过键值对来对字典进行遍历:
# 获取整个键值对
for item in D.items():
print(item)
# ('eggs',3)
# ('spam',2)
# ('ham',1)
# 分开获取键和值
for key,value in dic.items():
print(key,value)
# 'eggs' 3
# 'spam' 2
# 'ham' 1
通常我们不能在执行前预计将出现在一个字典中的数据。读取不存在的数据会出错,但是键不存在时通过get方法能够返回默认值——None或用户定义的默认值。
print(D.get('spam'))
# 2
print(D.get('toast'))
# None
print(D.get('toast',88)
# 88
待补充 287