本文主要内容
序列类型分类:
(1)容器序列、扁平序列
(2)可变序列、不可变序列
列表推导式
生成器表达式
元组拆包
切片
排序(list.sort方法和sorted函数)
bisect
文中代码均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高级
序列类型分类
所谓序列,即元素有序排列,python标准库用C实现了丰富的序列类型,按照序列中是否可存放不同类型的数据分为"容器序列"和"扁平序列"。
容器序列可以存放统统类型的数据,而扁平序列只能存放一种类型
容器序列:list、tuple、collections.deque
扁平序列:str、bytes、bytearray、memoryview、array.array
按照是否能修改的标准序列又可分为"可变序列"和"不可变序列":
可变序列:list、bytearrary、array.arrary、collections.deque和memoryview
不可变序列:tuple、str和bytes
由于可变序列继承自不可变序列,所以可变序列继承的方法也较多,下面看看它们包含的方法:
| 方法名 | 不可变序列 | 可变序列 |
| __contains__ | 有 | 有 |
| __iter__ | 有 | 有 |
| __len__ | 有 | 有 |
| __getitem__ | 有 | 有 |
| __reversed__ | 有 | 有 |
| index | 有 | 有 |
| count | 有 | 有 |
| __setitem__ | 有 | |
| __delitem__ | 有 | |
| insert | 有 | |
| append | 有 | |
| reverse | 有 | |
| extend | 有 | |
| pop | 有 | |
| remove | 有 | |
| __iadd__ | 有 |
我们以tuple和list类型为例,对比源代码中的方法,可以明显发现list的方法多于tuple:
列表推导式
# 列表推导式生成的是列表,会占用系统内存 # 基本语法 list_1 = [x for x in range(1, 20)] list_2 = [x ** 2 for x in range(1, 20)] print(list_1) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] print(list_2) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361] # 笛卡尔积型的列表推导式 list_3 = [(x, y) for x in range(1, 3) # 1,2 for y in range(7, 10)] # 7、8、9 # 该表达式会先将1分别和7、8、9组合,然后再拿2和7、8、9组合,共6对 print(list_3) # [(1, 7), (1, 8), (1, 9), (2, 7), (2, 8), (2, 9)] list_4 = [x+y for x in range(1, 3) for y in range(7, 10)] print(list_4) # [8, 9, 10, 9, 10, 11] # 还可以添加if语句 l = [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, -4, -7, -345, 46, -6, -45, 32, -8, -4, 67, -4] list_5 = [x for x in l if x > 0] # 只取出大于0的生成列表 print(list_5) # [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, 46, 32, 67] |
生成器表达式
# 虽然列表推导式可以用来初始化元组、数组或其他序列类型,但是列表推导式会直接生成列表,占用内存 # 而生成器遵守了迭代器协议,可以逐个产出元素,而不是先建立一个完整的列表 # 生成器表达式直接将推导式的方括号换成圆括号即可 g = (x for x in range(1, 10000)) print(g) # <generator object <genexpr> at 0x105c0efc0> :生成器对象 from collections import Iterable, Iterator if isinstance(g, Iterable): print("iterable") # 输出iterable: 说明生成器g是可迭代的 if isinstance(g, Iterator): print("iterator") # 输出iterator:说明生成器g是迭代器 |
下面我们来对比一下列表推导式和生成器的效率
# 比较列表推导式和生成器 import time start_time = time.time() l = [x for x in range(1000000)] print(time.time() - start_time) # 0.1361069679260254 start_time = time.time() g = (x for x in range(1000000)) print(time.time() - start_time) # 1.1205673217773438e-05 # 可见,生成器远快于推导式 |
元组拆包
# 我们经常这样给两个变量同时赋值 a, b = 1, 2 print(a, b) # 1 2 # 还可以这样 a, b = [1, 2] print(a, b) # 1 2 # 也可以这样 a, b = (1, 2) print(a, b) # 1 2 # 甚至可以这样 a, b = "ab" print(a, b) # a b ''' 像以上这样连续的赋值方式,右边可以使用逗号隔开;也可以是序列。 当拆包赋值的是序列时,python解释器会先找该序列中的__iter__方法,如果该方法不存在,则寻找__getitem__方法。 接下来说其他用法 ''' # 赋值后优雅地交换两个变量 a, b = (1, 2) a, b = b, a print(a, b) # 2 1 # 使用*号来处理多余的数据 a, b, *s = [1, 2, 3, 4, 5, 6, 7, 8, 9] print(a, b, s) # 1 2 [3, 4, 5, 6, 7, 8, 9] # 这样从第三个元素开始的所有值都赋给了s a, b, *s = (1, 2, 3, 4, 5, 6, 7, 8, 9) print(a, b, s) # 1 2 [3, 4, 5, 6, 7, 8, 9] # 注意,本来是元组,赋之后的s变成了列表. 如果s为空的话也会返回空列表 *s, a, b = (1, 2, 3, 4, 5, 6, 7, 8, 9) print(s, a, b) # [1, 2, 3, 4, 5, 6, 7] 8 9 # *s也可以放在前面 a, *s, b = (1, 2, 3, 4, 5, 6, 7, 8, 9) print(a, s, b) # 1 [2, 3, 4, 5, 6, 7, 8] 9 # *s也可以放在中间 # 嵌套元组拆包 a, b, (c, d) = (1, 2, (3, 4)) print(a, b, c, d) # 1 2 3 4 # 只要按照右边的形式就可赋值 a, b, *c = (1, 2, (3, 4)) print(a, b, c) # 1 2 [(3, 4)] |
1 ################################ 2 # 3 # 以下的例子用以说明拆包赋值时,解释器会按照__iter__、__getitem__的顺序调用类中的方法 4 # 5 ################################ 6 class Foo: 7 def __init__(self, s): 8 self.s = s 9 10 def __iter__(self): 11 print("iter") 12 return iter(self.s) 13 14 def __getitem__(self, item): 15 return self.s[item] 16 17 if __name__ == "__main__": 18 foo = Foo("sdfafasfasf") 19 a, b, *s = foo 20 print(a, b)