2.1 列表
- 序列结构有列表、元组、字符串,字典、集合以及 range 等。
- 列表、元组、字符串支持双向索引,第一个元素下标为 $0$,第二个元素下标为 $1$,以此类推;最后一个元素下标为 $-1$,倒数第二个元素下标为 $-2$,以此类推。
- 列表是内置有序可变序列,列表元素放在中括号 “[]” 中,用逗号分隔;
- 列表元素增加或删除时,列表对象自动进行扩展或收缩内存,保证元素之间没有缝隙;
- 列表中数据类型可各不相同;
[10, 20, 30, 40] ['crunchy frog', 'ram bladder', 'lark vomit'] ['spam', 2.0, 5, [10, 20]] [['file1', 200,7], ['file2', 260,9]]View Code
- 列表常用方法
2.1.1 列表创建与删除
- 列表赋值;
>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example'] >>> a_list = [] #创建空列表View Code
- 使用 list() 函数将 元组、range对象、字符串 或其他类型的 可迭代对象类型 的数据转换为列表;
>>> a_list = list((3,5,7,9,11)) >>> a_list [3, 5, 7, 9, 11] >>> list(range(1,10,2)) [1, 3, 5, 7, 9] >>> list('hello world') ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] >>> x = list() #创建空列表View Code
- 使用 del 命令删除整个列表;
>>> del a_list >>> a_list Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> a_list NameError: name 'a_list' is not definedView Code
2.1.2 列表元素的增加
- 使用 “+” 运算符将元素添加到列表中;
>>> aList = [3,4,5] >>> aList = aList + [7] >>> aList [3, 4, 5, 7]View Code
- 列表对象的 append() 方法,原地修改列表,在列表尾部添加元素,速度较快;
>>> aList.append(9) >>> aList [3, 4, 5, 7, 9]View Code
所谓 “原地”,是指不改变列表在内存中的首地址。
- 比较 “+” 和 append() 这两种方法的速度差异。
import time result = [] start = time.time() for i in range(10000): result = result + [i] print(len(result), ',', time.time()-start) result = [] start = time.time() for i in range(10000): result.append(i) print(len(result), ',', time.time()-start)View Code
- 修改值时,并不是真的直接修改变量的值,而是使变量指向新的值。
>>> a = [1,2,3] >>> id(a) #返回对象的内存地址 20230752 >>> a = [1,2] >>> id(a) 20338208View Code
- 通过下标来修改序列中元素的值或通过可变序列对象自身提供的方法来增加和删除元素时,序列对象在内存中的起始地址是不变的,仅仅是被改变值的元素地址发生变化,也就是所谓的“原地操作”。
>>> a = [1,2,4] >>> b = [1,2,3] >>> a == b False >>> id(a) == id(b) False >>> id(a[0]) == id(b[0]) True >>> a = [1,2,3] >>> id(a) 25289752 >>> a.append(4) >>> id(a) 25289752 >>> a.remove(3) >>> a [1, 2, 4] >>> id(a) 25289752 >>> a[0] = 5 >>> a [5, 2, 4] >>> id(a) 25289752View Code
- 列表对象的 extend() 方法可将另一个迭代对象的所有元素添加至该列表对象尾部。通过 extend() 方法来增加列表元素也不改变其内存首地址,属于原地操作。
>>> a.extend([7,8,9]) >>> a [5, 2, 4, 7, 8, 9] >>> aList.extend([11,13]) >>> aList [3, 4, 5, 7, 9, 11, 13] >>> aList.extend((15,17)) >>> aList [3, 4, 5, 7, 9, 11, 13, 15, 17] >>> id(a) 25289752View Code
- 列表对象的 insert() 方法将元素添加至列表的指定位置。
>>> aList.insert(3, 6) #在下标为3的位置插入元素6 >>> aList [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]View Code
- 使用乘法来扩展列表对象,将列表与整数相乘,生成一个新列表,新列表是原列表中元素的重复。
>>> aList = [3,5,7] >>> bList = aList >>> id(aList) 57091464 >>> id(bList) 57091464 >>> aList = aList*3 >>> aList [3, 5, 7, 3, 5, 7, 3, 5, 7] >>> bList [3,5,7] >>> id(aList) 57092680 >>> id(bList) 57091464View Code
- 使用 * 运算符 将包含列表的列表重复并创建新列表时,并不创建元素的复制,而是创建已有对象的引用。因此,当修改其中一个值时,相应的引用也会被修改。
>>> x = [[None] * 2] * 3 >>> x [[None, None], [None, None], [None, None]] >>> x[0][0] = 5 >>> x [[5, None], [5, None], [5, None]] >>> x = [[1,2,3]] * 3 >>> x[0][0] = 10 >>> x [[10, 2, 3], [10, 2, 3], [10, 2, 3]]View Code
2.1.3 列表元素的删除
- del 命令删除列表中的指定位置上的元素。
>>> a_list = [3,5,7,9,11] >>> del a_list[1] >>> a_list [3, 7, 9, 11]View Code
- 列表对象的 pop() 方法删除并返回指定(默认为最后一个)位置上的元素,如果给定的索引超出列表的范围则抛出异常。
>>> a_list = list((3,5,7,9,11)) >>> a_list.pop() 11 >>> a_list [3, 5, 7, 9] >>> a_list.pop(1) 5 >>> a_list [3, 7, 9]View Code
- 列表对象的 remove() 方法删除首次出现的指定元素,如果列表中不存在要删除的元素,则抛出异常。
>>> a_list = [3,5,7,9,7,11] >>> a_list.remove(7) >>> a_list [3, 5, 9, 7, 11]View Code
- 删除对比
>>> x = [1,2,1,2,1,2,1,2,1] >>> for i in x: if i == 1: x.remove(i) >>> x [2, 2, 2, 2] >>> x = [1,2,1,2,1,1,1] >>> for i in x: if i == 1: x.remove(i) >>> x [2, 2, 1]View Code
正确的代码
>>> x = [1,2,1,2,1,1,1] >>> for i in x[::]: #切片 if i == 1: x.remove(i) #或者: >>> x = [1,2,1,2,1,1,1] >>> for i in range(len(x)-1,-1,-1): if x[i]==1: del x[i]View Code
2.1.4 列表元素访问与计数
- 使用下标直接访问列表元素,如果指定下标不存在,则抛出异常。
>>> aList[3] 6 >>> aList[3] = 5.5 >>> aList [3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17] >>> aList[15] Traceback (most recent call last): File "<pyshell#34>", line 1, in <module> aList[15] IndexError: list index out of rangeView Code
- 使用列表对象的 index() 方法获取指定元素首次出现的下标,若列表对象中不存在指定元素,则抛出异常。
>>> aList [3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17] >>> aList.index(7) 4 >>> aList.index(100) Traceback (most recent call last): File "<pyshell#36>", line 1, in <module> aList.index(100) ValueError: 100 is not in listView Code
- 使用列表对象的 count() 方法统计指定元素在列表对象中出现的次数
>>> aList [3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17] >>> aList.count(7) 1 >>> aList.count(0) 0 >>> aList.count(8) 0View Code
2.1.5 成员资格判断
- 如果需要判断列表中是否存在指定的值,可以使用 count() 方法,如果存在则返回大于 0 的数,如果返回 0 则表示不存在。或者,使用更加简洁的“in”关键字来判断一个值是否存在于列表中,返回结果为“True”或“False”。
>>> aList [3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17] >>> 3 in aList True >>> 18 in aList False >>> bList = [[1], [2], [3]] >>> 3 in bList False >>> 3 not in bList True >>> [3] in bList True >>> aList = [3, 5, 7, 9, 11] >>> bList = ['a', 'b', 'c', 'd'] >>> (3, 'a') in zip(aList, bList) True >>> for a, b in zip(aList, bList): print(a, b)View Code
2.1.6 切片操作
- 切片使用 2 个冒号分隔的3个数字来完成,第一个数字表示切片开始位置(默认为0),第二个数字表示切片截止(但不包含)位置(默认为列表长度),第三个数字表示切片的步长(默认为1),当步长省略时可以顺便省略最后一个冒号。
- 切片操作不会因为下标越界而抛出异常,而是简单地在列表尾部截断或者返回一个空列表
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> aList[::] #返回包含元素的新列表 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> aList[::-1] #逆序的所有元素 [17, 15, 13, 11, 9, 7, 6, 5, 4, 3] >>> aList[::2] #偶数位置,隔一个取一个 [3, 5, 7, 11, 15] >>> aList[1::2] #奇数位置,隔一个取一个 [4, 6, 9, 13, 17] >>> aList[3::] #从下标3开始的所有元素 [6, 7, 9, 11, 13, 15, 17] >>> aList[3:6] #下标在[3, 6)之间的所有元素 [6, 7, 9] >>> aList[0:100:1] #前100个元素,自动截断 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> a[100:] #下标100之后的所有元素,自动截断 [] >>> x[100] #直接使用下标访问会发生越界 IndexError: list index out of rangeView Code
- 可以使用切片来原地修改列表内容
>>> aList = [3, 5, 7] >>> aList[len(aList):] = [9] #在尾部追加元素 >>> aList [3, 5, 7, 9] >>> aList[:3] = [1, 2, 3] #替换前3个元素 >>> aList [1, 2, 3, 9] >>> aList[:3] = [] #删除前3个元素 >>> aList [9] >>> aList = list(range(10)) >>> aList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> aList[::2] = [0]*5 #替换偶数位置上的元素 >>> aList [0, 1, 0, 3, 0, 5, 0, 7, 0, 9] >>> aList[::2] = [0]*3 #切片不连续,两个元素个数必须一样多 ValueError: attempt to assign sequence of size 3 to extended slice of size 5View Code
- 使用 del 与切片结合来删除列表元素
>>> aList = [3,5,7,9,11] >>> del aList[:3] #删除前3个元素 >>> aList [9, 11] >>> aList = [3,5,7,9,11] >>> del aList[::2] #删除偶数位置上的元素 >>> aList [5, 9]View Code
- 切片返回的是列表元素的浅复制
>>> aList = [3, 5, 7] >>> bList = aList #bList与aList指向同一个内存 >>> bList [3, 5, 7] >>> bList[1] = 8 #修改其中一个对象会影响另一个 >>> aList [3, 8, 7] >>> aList == bList #两个列表的元素完全一样 True >>> aList is bList #两个列表是同一个对象 True >>> id(aList) #内存地址相同 19061816 >>> id(bList) 19061816View Code
- 所谓浅复制,是指生成一个新的列表,并且把原列表中所有元素的引用都复制到新列表中。
>>> aList = [3, 5, 7] >>> bList = aList[::] #切片,浅复制 >>> aList == bList #两个列表的元素完全一样 True >>> aList is bList #但不是同一个对象 False >>> id(aList) == id(bList) #内存地址不一样 False >>> bList[1] = 8 #修改其中一个不会影响另一个 >>> bList [3, 8, 7] >>> aList [3, 5, 7]View Code
2.1.7 列表排序
- 使用列表对象的 sort 方法进行原地排序,支持多种不同的排序方法。
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> import random >>> random.shuffle(aList) >>> aList [3, 4, 15, 11, 9, 17, 13, 6, 7, 5] >>> aList.sort() #默认是升序排序 >>> aList.sort(reverse = True) #降序排序 >>> aList [17, 15, 13, 11, 9, 7, 6, 5, 4, 3] >>> aList.sort(key = lambda x:len(str(x))) #按转换成字符串的长度排序 >>> aList [9, 7, 6, 5, 4, 3, 17, 15, 13, 11]View Code
- 使用内置函数sorted对列表进行排序并返回新列表
>>> aList [9, 7, 6, 5, 4, 3, 17, 15, 13, 11] >>> sorted(aList) #升序排序 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> sorted(aList,reverse = True) #降序排序 [17, 15, 13, 11, 9, 7, 6, 5, 4, 3]View Code
- 列表对象的 reverse 方法将元素原地逆序
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> aList.reverse() >>> aList [17, 15, 13, 11, 9, 7, 6, 5, 4, 3]View Code
- 内置函数 reversed 方法对列表元素进行逆序排列并返回迭代对象
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] >>> newList = reversed(aList) #返回reversed对象 >>> list(newList) #把reversed对象转换成列表 [17, 15, 13, 11, 9, 7, 6, 5, 4, 3] >>> for i in newList: print(i, end=' ') #这里没有输出内容 #迭代对象已遍历结束 >>> newList = reversed(aList) #重新创建reversed对象 >>> for i in newList: print(i, end=' ') 17 15 13 11 9 7 6 5 4 3View Code
2.1.8 用于序列操作的常用内置函数
- len(列表):返回列表中的元素个数,同样适用于元组、字典、集合、字符串等。
- max(列表)、 min(列表):返回列表中的最大或最小元素,同样适用于元组、字典、集合、range对象等。
- sum(列表):对列表的元素进行求和运算,对非数值型列表运算需要指定start参数,同样适用于元组、range。
>>> sum(range(1, 11)) #sum()函数的start参数默认为0 55 >>> sum(range(1, 11), 5) #指定start参数为5,等价于5+sum(range(1,11)) 60 >>> sum([[1, 2], [3], [4]], []) #这个操作占用空间较大,慎用 [1, 2, 3, 4]View Code
- zip() 函数返回可迭代的 zip 对象。
>>> aList = [1, 2, 3] >>> bList = [4, 5, 6] >>> cList = zip(a, b) #返回zip对象 >>> cList <zip object at 0x0000000003728908> >>> list(cList) #把zip对象转换成列表 [(1, 4), (2, 5), (3, 6)]View Code
- enumerate(列表):枚举列表元素,返回枚举对象,其中每个元素为包含下标和值的元组。该函数对元组、字符串同样有效。
>>> for item in enumerate('abcdef'): print(item)View Code
2.1.9 列表推导式
- 列表推导式使用非常简洁的方式来快速生成满足特定需求的列表
>>> aList = [x*x for x in range(10)]
相当于
>>> aList = [] >>> for x in range(10): aList.append(x*x)
也相当于
>>> aList = list(map(lambda x: x*x, range(10)))
例子:
>>> sum([2**i for i in range(64)]) 18446744073709551615
- 使用列表推导式实现嵌套列表的平铺
>>> vec = [[1,2,3], [4,5,6], [7,8,9]] >>> [num for elem in vec for num in elem] [1, 2, 3, 4, 5, 6, 7, 8, 9]View Code
相当于
>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> result = [] >>> for elem in vec: for num in elem: result.append(num) >>> result [1, 2, 3, 4, 5, 6, 7, 8, 9]View Code
不使用列表推导式:
>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> sum(vec, []) [1, 2, 3, 4, 5, 6, 7, 8, 9] #或 >>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> from itertools import chain >>> list(chain(*vec)) [1, 2, 3, 4, 5, 6, 7, 8, 9]View Code
- 列出当前文件夹下所有 Python 源文件
>>> import os >>> [filename for filename in os.listdir('.') if filename.endswith(('.py', '.pyw'))]View Code
- 过滤不符合条件的元素
>>> aList = [-1,-4,6,7.5,-2.3,9,-11] >>> [i for i in aList if i>0] [6, 7.5, 9]View Code
例子:已知有一个包含一些同学成绩的字典,计算成绩的最高分、最低分、平均分,并查找所有最高分同学。
>>> scores = {"Zhang San": 45, "Li Si": 78, "Wang Wu": 40, "Zhou Liu": 96, "Zhao Qi": 65, "Sun Ba": 90, "Zheng Jiu": 78, "Wu Shi": 99, "Dong Shiyi": 60} >>> highest = max(scores.values()) >>> lowest = min(scores.values()) >>> average = sum(scores.values())*1.0/len(scores) >>> highest, lowest, average 99 40 72.33333333333333 >>> highestPerson = [name for name, score in scores.items() if score == highest] >>> highestPerson ['Wu Shi']View Code
- 在列表推导式中使用多个循环,实现多序列元素的任意组合,并且可以结合条件语句过滤特定元素
>>> [(x, y) for x in range(3) for y in range(3)] [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] >>> [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y] [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]View Code
- 使用列表推导式实现矩阵转置
>>>matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]] >>> [[row[i] for row in matrix] for i in range(4)] [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]View Code
- 也可以使用内置函数来实现矩阵转置
>>>list(zip(*matrix)) #序列解包 [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]View Code
- 列表推导式中可以使用函数或复杂表达式
>>> def f(v): if v%2 == 0: v = v**2 else: v = v+1 return v >>> [f(v) for v in [2, 3, 4, -1] if v>0] [4, 4, 16] >>> [v**2 if v%2 == 0 else v+1 for v in [2, 3, 4, -1] if v>0] [4, 4, 16]View Code
- 列表推导式支持文件对象迭代
>>> with open('C:\\RHDSetup.log', 'r') as fp: print([line for line in fp])View Code
- 使用列表推导式生成 100 以内的所有素数
>>> [p for p in range(2, 100) if 0 not in [p%d for d in range(2, int(p**0.5)+1)]] [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]View Code
2.1.10 使用列表实现向量运算
>>> import random >>> x = [random.randint(1,100) for i in range(10)] #生成随机数 >>> list(map(lambda i: i+5, x)) #所有元素同时加5 >>> x = [random.randint(1,10) for i in range(10)] >>> y = [random.randint(1,10) for i in range(10)] >>> import operator >>> sum(map(operator.mul, x, y)) #向量内积 >>> sum((i*j for i, j in zip(x, y))) #向量内积 >>> list(map(operator.add, x, y)) #两个等长的向量对应元素相加View Code
2.2 元组
2.2.1 元组创建与删除
- 使用“=”将一个元组赋值给变量
>>>a_tuple = ('a', 'b', 'mpilgrim', 'z', 'example') >>> a_tuple ('a', 'b', 'mpilgrim', 'z', 'example') >>> a = (3) >>> a 3 >>> a = (3,) #包含一个元素的元组,最后必须多写个逗号 >>> a (3,) >>> a = 3, #也可以这样创建元组 >>> a (3,) >>> x = () #空元组View Code
- 使用 tuple 函数将其他序列转换为元组
>>> tuple('abcdefg') #把字符串转换为元组 ('a', 'b', 'c', 'd', 'e', 'f', 'g') >>> aList [-1, -4, 6, 7.5, -2.3, 9, -11] >>> tuple(aList) #把列表转换为元组 (-1, -4, 6, 7.5, -2.3, 9, -11) >>> s = tuple() #空元组 >>> s ()View Code
- 使用 del 可以删除元组对象,不能删除元组中的元素
2.2.2 元组与列表的区别
- 元组中的数据一旦定义就不允许更改。
- 元组没有 append()、extend() 和 insert() 等方法,无法向元组中添加元素。
- 元组没有 remove() 或 pop() 方法,也无法对元组元素进行 del 操作,不能从元组中删除元素。
- 从效果上看,tuple( ) 冻结列表,而 list( ) 融化元组。
2.2.2 元组的优点
- 元组的速度比列表更快。
- 元组对不需要改变的数据进行“写保护”将使得代码更加安全。
- 元组可用作字典键(不可变数据的元组)。列表永远不能当做字典键使用,因为列表不是不可变的。
2.2.3 序列解包
- 使用序列解包功能对多个变量同时赋值
>>> x, y, z = 1, 2, 3 #多个变量同时赋值 >>> v_tuple = (False, 3.5, 'exp') >>> (x, y, z) = v_tuple >>> x, y, z = v_tuple >>> x, y, z = range(3) #可以对range对象进行序列解包 >>> x, y, z = iter([1, 2, 3]) #使用迭代器对象进行序列解包 >>> x, y, z = map(str, range(3)) #使用可迭代的map对象进行序列解包 >>> a, b = b, a #交换两个变量的值 >>> x, y, z = sorted([1, 3, 2]) #sorted()函数返回排序后的列表 >>> a, b, c = 'ABC' #字符串也支持序列解包View Code
- 序列解包对于列表和字典同样有效
>>> s = {'a':1, 'b':2, 'c':3} >>> b, c, d = s.items() >>> b ('c', 3) >>> b, c, d = s #使用字典时不用太多考虑元素的顺序 >>> b 'c' >>> b, c, d = s.values() >>> print(b, c, d) 1 3 2View Code
- 序列解包遍历多个序列
>>> keys = ['a', 'b', 'c', 'd'] >>> values = [1, 2, 3, 4] >>> for k, v in zip(keys, values): print((k, v), end=' ') ('a', 1) ('b', 2) ('c', 3) ('d', 4)View Code
- 序列解包遍历 enumerate 对象
>>> x = ['a', 'b', 'c'] >>> for i, v in enumerate(x): print('The value on position {0} is {1}'.format(i,v)) The value on position 0 is a The value on position 1 is b The value on position 2 is c >>> aList = [1,2,3] >>> bList = [4,5,6] >>> cList = [7,8,9] >>> dList = zip(aList, bList, cList) >>> for index, value in enumerate(dList): print(index, ':', value) 0 : (1, 4, 7) 1 : (2, 5, 8) 2 : (3, 6, 9)View Code
- Python 3.5还支持下面用法的序列解包
>>> print(*[1, 2, 3], 4, *(5, 6)) 1 2 3 4 5 6 >>> *range(4),4 (0, 1, 2, 3, 4) >>> {*range(4), 4, *(5, 6, 7)} {0, 1, 2, 3, 4, 5, 6, 7} >>> {'x': 1, **{'y': 2}} {'y': 2, 'x': 1}View Code
2.2.4 生成器推导式
- 生成器推导式的结果是一个生成器对象。使用生成器对象的元素时,可以根据需要将其转化为列表或元组,也可以使用生成器对象 __next__() 方法或内置函数 next() 进行遍历,或者直接将其作为迭代器对象来使用。
- 生成器对象具有惰性求值的特点,只在需要时生成新元素,比列表推导式具有更高的效率,空间占用非常少,尤其适合大数据处理的场合。
- 不管用哪种方法访问生成器对象,都无法再次访问已访问过的元素。
- 使用生成器对象__next__()方法或内置函数next()进行遍历
>>> g = ((i+2)**2 for i in range(10)) #创建生成器对象 >>> g <generator object <genexpr> at 0x0000000003095200> >>> tuple(g) #将生成器对象转换为元组 (4, 9, 16, 25, 36, 49, 64, 81, 100, 121) >>> list(g) #生成器对象已遍历结束,没有元素了 [] >>> g = ((i+2)**2 for i in range(10)) #重新创建生成器对象 >>> g.__next__() #使用生成器对象的__next__()方法获取元素 4 >>> g.__next__() #获取下一个元素 9 >>> next(g) #使用函数next()获取生成器对象中的元素 16View Code
- 使用生成器对象 __next__() 方法或内置函数 next() 进行遍历
>>> g = ((i+2)**2 for i in range(10)) #创建生成器对象 >>> g <generator object <genexpr> at 0x0000000003095200> >>> tuple(g) #将生成器对象转换为元组 (4, 9, 16, 25, 36, 49, 64, 81, 100, 121) >>> list(g) #生成器对象已遍历结束,没有元素了 [] >>> g = ((i+2)**2 for i in range(10)) #重新创建生成器对象 >>> g.__next__() #使用生成器对象的__next__()方法获取元素 4 >>> g.__next__() #获取下一个元素 9 >>> next(g) #使用函数next()获取生成器对象中的元素 16View Code
- 使用 for 循环直接迭代生成器对象中的元素
>>> g = ((i+2)**2 for i in range(10)) >>> for item in g: #使用循环直接遍历生成器对象中的元素 print(item, end=' ') 4 9 16 25 36 49 64 81 100 121 >>> x = filter(None, range(20)) #filter对象也具有类似的特点 >>> 5 in x True >>> 2 in x #不可再次访问已访问过的元素 False >>> x = map(str, range(20)) #map对象也具有类似的特点 >>> '0' in x True >>> '0' in x #不可再次访问已访问过的元素 FalseView Code
2.3 字典
- 字典是无序可变序列。
- 定义字典时,每个元素的键和值用冒号分隔,元素之间用逗号分隔,所有的元素放在一对大括号“{}”中。
- 字典中的键可以为任意不可变数据,比如整数、实数、复数、字符串、元组等等。
- globals() 返回包含当前作用域内所有全局变量和值的字典
- locals() 返回包含当前作用域内所有局部变量和值的字典
2.3.1 字典创建与删除
- 使用=将一个字典赋值给一个变量
>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'} >>> a_dict {'database': 'mysql', 'server': 'db.diveintopython3.org'} >>> x = {} #空字典 >>> x {}View Code
- 使用 dict 利用已有数据创建字典:
>>> keys = ['a', 'b', 'c', 'd'] >>> values = [1, 2, 3, 4] >>> dictionary = dict(zip(keys, values)) >>> dictionary {'a': 1, 'c': 3, 'b': 2, 'd': 4} >>> x = dict() #空字典 >>> x {}View Code
- 以给定内容为键,创建值为空的字典
>>> adict = dict.fromkeys(['name', 'age', 'sex']) >>> adict {'age': None, 'name': None, 'sex': None}View Code
- 可以使用 del 删除整个字典
2.3.2 字典元素的读取
- 以键作为下标可以读取字典元素,若键不存在则抛出异常
>>> aDict = {'name':'Dong', 'sex':'male', 'age':37} >>> aDict['name'] 'Dong' >>> aDict['tel'] #键不存在,抛出异常 Traceback (most recent call last): File "<pyshell#53>", line 1, in <module> aDict['tel'] KeyError: 'tel'View Code
- 使用字典对象的 get 方法获取指定键对应的值,并且可以在键不存在的时候返回指定值。
>>> print(aDict.get('address')) None >>> print(aDict.get('address', 'SDIBT')) SDIBT >>> aDict['score'] = aDict.get('score',[]) >>> aDict['score'].append(98) >>> aDict['score'].append(97) >>> aDict {'age': 37, 'score': [98, 97], 'name': 'Dong', 'sex': 'male'}View Code
- 使用字典对象的 items() 方法可以返回字典的键、值对列表
- 使用字典对象的 keys() 方法可以返回字典的键列表
- 使用字典对象的 values() 方法可以返回字典的值列表
>>> aDict={'name':'Dong', 'sex':'male', 'age':37} >>> for item in aDict.items(): #输出字典中所有元素 print(item) ('age', 37) ('name', 'Dong') ('sex', 'male') >>> for key in aDict: #不加特殊说明,默认输出键 print(key) age name sex >>> for key, value in aDict.items(): #序列解包用法 print(key, value) age 37 name Dong sex male >>> aDict.keys() #返回所有键 dict_keys(['name', 'sex', 'age']) >>> aDict.values() #返回所有值 dict_values(['Dong', 'male', 37])View Code
- 使用字典对象的update方法将另一个字典的键、值对添加到当前字典对象;
2.3.3 字典元素的添加与修改
- 当以指定键为下标为字典赋值时,若键存在,则可以修改该键的值;若不存在,则表示添加一个键、值对;
>>> aDict['age'] = 38 #修改元素值 >>> aDict {'age': 38, 'name': 'Dong', 'sex': 'male'} >>> aDict['address'] = 'SDIBT' #增加新元素 >>> aDict {'age': 38, 'address': 'SDIBT', 'name': 'Dong', 'sex': 'male'}View Code
- 使用字典对象的 update 方法将另一个字典的键、值对添加到当前字典对象
>>> aDict {'age': 37, 'score': [98, 97], 'name': 'Dong', 'sex': 'male'} >>> aDict.items() dict_items([('age', 37), ('score', [98, 97]), ('name', 'Dong'), ('sex', 'male')]) >>> aDict.update({'a':'a','b':'b'}) >>> aDict {'a': 'a', 'score': [98, 97], 'name': 'Dong', 'age': 37, 'b': 'b', 'sex': 'male'}View Code
- 使用 del 删除字典中指定键的元素;
- 使用字典对象的 clear() 方法来删除字典中所有元素;
- 使用字典对象的 pop() 方法删除并返回指定键的元素;
- 使用字典对象的 popitem() 方法删除并返回字典中的一个元素;
2.3.5 有序字典
- Python内置字典是无序的,如需一个可以记住元素插入顺序的字典,可以使用 collections.OrderedDict;
>>> x = dict() #无序字典 >>> x['a'] = 3 >>> x['b'] = 5 >>> x['c'] = 8 >>> x {'b': 5, 'c': 8, 'a': 3} >>> import collections >>> x = collections.OrderedDict() #有序字典 >>> x['a'] = 3 >>> x['b'] = 5 >>> x['c'] = 8 >>> x OrderedDict([('a', 3), ('b', 5), ('c', 8)])View Code
2.3.6 字典推导式
>>> s = {x:x.strip() for x in (' he ', 'she ', ' I')} >>> s {' he ': 'he', ' I': 'I', 'she ': 'she'} >>> for k, v in s.items(): print(k, ':', v) he : he I : I she : she >>> {i:str(i) for i in range(1, 5)} {1: '1', 2: '2', 3: '3', 4: '4'} >>> x = ['A', 'B', 'C', 'D'] >>> y = ['a', 'b', 'b', 'd'] >>> {i:j for i,j in zip(x,y)} {'A': 'a', 'C': 'b', 'B': 'b', 'D': 'd'}View Code
2.4 集合
- 集合是无序可变序列,使用一对大括号界定,元素不可重复,同一个集合中每个元素都是唯一的;
- 集合中只能包含数字、字符串、元组等不可变类型(或者说可哈希)的数据,而不能包含列表、字典、集合等可变类型的数据;
2.4.1 集合的创建与删除
- 直接将集合赋值给变量
>>> a = {3, 5} >>> a.add(7) #向集合中添加元素 >>> a {3, 5, 7}View Code
- 使用 set 将其他类型数据转换为集合
>>> a_set = set(range(8,14)) >>> a_set {8, 9, 10, 11, 12, 13} >>> b_set = set([0, 1, 2, 3, 0, 1, 2, 3, 7, 8]) #自动去除重复 >>> b_set {0, 1, 2, 3, 7, 8} >>> c_set = set() #空集合 >>> c_set set()View Code
- 使用 del 删除整个集合
- 当不再使用某个集合时,可以使用 del 命令删除整个集合。集合对象的 pop() 方法弹出并删除其中一个元素,remove() 方法直接删除指定元素,clear() 方法清空集合。
2.4.2 集合操作
Python集合支持交集、并集、差集等运算
>>> a_set = set([8, 9, 10, 11, 12, 13]) >>> b_set = {0, 1, 2, 3, 7, 8} >>> a_set | b_set #并集 {0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13} >>> a_set.union(b_set) #并集 {0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13} >>> a_set & b_set #交集 {8} >>> a_set.intersection(b_set) #交集 {8} >>> a_set.difference(b_set) #差集 {9, 10, 11, 12, 13} >>> a_set - b_set {9, 10, 11, 12, 13}View Code
>>> a_set.symmetric_difference(b_set) #对称差集 {0, 1, 2, 3, 7, 9, 10, 11, 12, 13} >>> a_set ^ b_set {0, 1, 2, 3, 7, 9, 10, 11, 12, 13} >>> x = {1, 2, 3} >>> y = {1, 2, 5} >>> z = {1, 2, 3, 4} >>> x.issubset(y) #测试是否为子集 False >>> x.issubset(z) True >>> {3} & {4} set() >>> {3}.isdisjoint({4}) #如果两个集合的交集为空,返回True TrueView Code
- 集合包含关系测试
>>> x = {1, 2, 3} >>> y = {1, 2, 5} >>> z = {1, 2, 3, 4} >>> x < y #比较集合大小/包含关系 False >>> x < z #真子集 True >>> y < z False >>> {1, 2, 3} <= {1, 2, 3} #子集 TrueView Code
- 使用集合快速提取序列中单一元素
>>> import random >>> listRandom = [random.choice(range(10000)) for i in range(100)] >>> noRepeat = [] >>> for i in listRandom : if i not in noRepeat : noRepeat.append(i) >>> len(listRandom) >>> len(noRepeat) >>> newSet = set(listRandom)View Code
2.5 再谈内置方法sorted()
- 列表对象提供了 sort() 方法支持原地排序,而内置函数 sorted() 返回新的列表,并不对原列表进行任何修改。
- sorted() 方法可以对列表、元组、字典、range 对象等进行排序。
- 列表的 sort() 方法和内置函数 sorted() 都支持 key 参数实现复杂排序要求。
>>> persons = [{'name':'Dong', 'age':37}, {'name':'Zhang', 'age':40}, {'name':'Li', 'age':50}, {'name':'Dong', 'age':43}] >>> print(persons) [{'age': 37, 'name': 'Dong'}, {'age': 40, 'name': 'Zhang'}, {'age': 50, 'name': 'Li'}, {'age': 43, 'name': 'Dong'}] #使用key来指定排序依据,先按姓名升序排序,姓名相同的按年龄降序排序 >>> print(sorted(persons, key=lambda x:(x['name'], -x['age']))) [{'age': 43, 'name': 'Dong'}, {'age': 37, 'name': 'Dong'}, {'age': 50, 'name': 'Li'}, {'age': 40, 'name': 'Zhang'}] >>> phonebook = {'Linda':'7750', 'Bob':'9345', 'Carol':'5834'} >>> from operator import itemgetter >>> sorted(phonebook.items(), key=itemgetter(1)) #按字典中元素值进行排序 [('Carol', '5834'), ('Linda', '7750'), ('Bob', '9345')] >>> sorted(phonebook.items(), key=itemgetter(0)) #按字典中元素的键进行排序 [('Bob', '9345'), ('Carol', '5834'), ('Linda', '7750')] >>> gameresult = [['Bob', 95.0, 'A'], ['Alan', 86.0, 'C'], ['Mandy', 83.5, 'A'], ['Rob', 89.3, 'E']] >>> sorted(gameresult, key=itemgetter(0, 1)) #按姓名升序,姓名相同按分数升序排序 [['Alan', 86.0, 'C'], ['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Rob', 89.3, 'E']] >>> sorted(gameresult, key=itemgetter(1, 0)) #按分数升序,分数相同的按姓名升序排序 [['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E'], ['Bob', 95.0, 'A']] >>> sorted(gameresult, key=itemgetter(2, 0)) #按等级升序,等级相同的按姓名升序排序 [['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E']] >>> gameresult = [{'name':'Bob', 'wins':10, 'losses':3, 'rating':75.0}, {'name':'David', 'wins':3, 'losses':5, 'rating':57.0}, {'name':'Carol', 'wins':4, 'losses':5, 'rating':57.0}, {'name':'Patty', 'wins':9, 'losses':3, 'rating':72.8}] >>> sorted(gameresult, key=itemgetter('wins', 'name')) #按'wins'升序,该值相同的按'name'升序排序 [{'wins': 3, 'rating': 57.0, 'name': 'David', 'losses': 5}, {'wins': 4, 'rating': 57.0, 'name': 'Carol', 'losses': 5}, {'wins': 9, 'rating': 72.8, 'name': 'Patty', 'losses': 3}, {'wins': 10, 'rating': 75.0, 'name': 'Bob', 'losses': 3}]View Code
- 根据另外一个列表的值来对当前列表元素进行排序
>>> list1 = ["what", "I'm", "sorting", "by"] >>> list2 = ["something", "else", "to", "sort"] >>> pairs = zip(list1, list2) >>> pairs = sorted(pairs) >>> pairs [("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')] >>> result = [x[1] for x in pairs] >>> result ['else', 'sort', 'to', 'something']View Code
2.6 复杂数据结构
2.6.1 堆
>>> import heapq #heapq和random是Python标准库 >>> import random >>> data=range(10) >>> data [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> random.choice(data) #随机选择一个元素 9 >>> random.shuffle(data) #随机打乱顺序 >>> data [6, 1, 3, 4, 9, 0, 5, 2, 8, 7] >>> heap=[] >>> for n in data: #建堆 heapq.heappush(heap,n) >>> heap [0, 2, 1, 4, 7, 3, 5, 6, 8, 9] >>> heapq.heappush(heap,0.5) #入堆,自动重建 >>> heap [0, 0.5, 1, 4, 2, 3, 5, 6, 8, 9, 7] >>> heapq.heappop(heap) #出堆,自动重建 0 >>> myheap=[1,2,3,5,7,8,9,4,10,333] >>> heapq.heapify(myheap) #建堆 >>> myheap [1, 2, 3, 4, 7, 8, 9, 5, 10, 333] >>> heapq.heapreplace(myheap,6) #弹出最小元素,同时插入新元素 1 >>> myheap [2, 4, 3, 5, 7, 8, 9, 6, 10, 333] >>> heapq.nlargest(3, myheap) #返回前3个最大的元素 [333, 10, 9] >>> heapq.nsmallest(3, myheap) #返回前3个最小的元素 [2, 3, 4]View Code
2.6.2 队列
>>> import queue #queue是Python标准库 >>> q=queue.Queue() >>> q.put(0) #入队 >>> q.put(1) >>> q.put(2) >>> q.queue deque([0, 1, 2]) >>> q.get() #出队 0 >>> q.queue #查看队列中的元素 deque([1, 2]) >>> q.get() 1 >>> q.queue deque([2])View Code
- queue模块还提供了“后进先出”队列和优先级队列。
>>> from queue import Queue #LILO队列 >>> q = Queue() #创建队列对象 >>> q.put(0) #在队列尾部插入元素 >>> q.put(1) >>> q.put(2) >>> print(q.queue) #查看队列中所有元素 deque([0, 1, 2]) >>> q.get() #返回并删除队列头部元素 0 >>> q.get() 1View Code
>>> from queue import LifoQueue #LIFO队列 >>> q = LifoQueue() #创建LIFO队列对象 >>> q.put(1) #在队列尾部插入元素 >>> q.put(2) >>> q.put(3) >>> q.queue #查看队列中所有元素 [1, 2, 3] >>> q.get() #返回并删除队列尾部元素 3 >>> q.get() 2 >>> q.queue [1] >>> q.get() #对空队列调用get()方法会阻塞当前线程View Code
>>> from queue import PriorityQueue #优先级队列 >>> q = PriorityQueue() #创建优先级队列对象 >>> q.put(3) #插入元素 >>> q.put(8) #插入元素 >>> q.put(100) >>> q.queue #查看优先级队列中所有元素 [3, 8, 100] >>> q.put(1) #插入元素,自动调整优先级队列 >>> q.put(2) >>> q.queue [1, 2, 100, 8, 3] >>> q.get() #返回并删除优先级最低的元素 1 >>> q.get() #请多执行几次该语句并观察返回的数据 2View Code
- Python标准库collections提供了双端队列deque
>>> from collections import deque >>> q = deque(maxlen=5) #创建双端队列 >>> for item in [3, 5, 7, 9, 11]: #添加元素 q.append(item) >>> q.append(13) #队列满,自动溢出 >>> q.append(15) >>> q deque([7, 9, 11, 13, 15], maxlen=5) >>> q.appendleft(5) #从左侧添加元素,右侧自动溢出 >>> q deque([5, 7, 9, 11, 13], maxlen=5)View Code
2.6.3 栈
- 栈是一种“后进先出(LIFO)”或“先进后出(FILO)”的数据结构。
- Python列表本身就可以实现栈结构的基本操作。例如,列表对象的append()方法是在列表尾部追加元素,类似于入栈操作;pop()方法默认是弹出并返回列表的最后一个元素,类似于出栈操作。
- 但是直接使用Python列表对象模拟栈操作并不是很方便,例如当列表为空时再执行pop()出栈操作时则会抛出一个不很友好的异常;另外,也无法限制栈的大小。
- 可以直接使用列表来实现栈结构
>>> myStack = [] >>> myStack.append(3) >>> myStack.append(5) >>> myStack.append(7) >>> myStack [3, 5, 7] >>> myStack.pop() 7 >>> myStack.pop() 5 >>> myStack.pop() 3 >>> myStack.pop() 出错View Code
- 封装列表实现栈结构
class Stack: def __init__(self, size = 10): self._content = [] #使用列表存放栈的元素 self._size = size #初始栈大小 self._current = 0 #栈中元素个数初始化为0 def empty(self): self._content = [] self._current = 0 def isEmpty(self): if not self._content: return True else: return FalseView Code