文章目录
主要讲述python语言内建功能, 常见的数据结构主要: 元组, 列表, 字典和集合.
1. 元组
元组是固定长度, 不可变的python对象序列. 创建元组很简单的方法就是用逗号隔开:
In [1]: a = 4, 5, 6
In [2]: a
Out[2]: (4, 5, 6)
当需要表达更复杂的元组时,需要加上括号:
In [3]: nest = (4, 5, 6), (7, 8)
In [4]: nest
Out[4]: ((4, 5, 6), (7, 8))
可以使用tuple函数将任意序列或迭代器转换为元组:
In [5]: tuple([4, 0, 2])
Out[5]: (4, 0, 2)
In [6]: a = tuple('string')
In [7]: a
Out[7]: ('s', 't', 'r', 'i', 'n', 'g')
元组的元素可以通过[]来获取,在大多数序列类型中都可以使用这个方法:
In [8]: a[0]
Out[8]: 's'
虽然对象元组中存储的对象其自身是可以改变的, 但是元组一旦创建, 各个位置上的对象是无法被修改的:
In [9]: a = tuple(['foo', [1, 2], True])
In [10]: a[2] = False
上述会报TypeError的错误. 但是如果一个元组的对象是可变的, 例如列表, 那么就可以在列表内部进行修改:
In [11]: a[1].append(3)
In [12]: a
Out[12]: ('foo', [1, 2,3], True)
可以使用+来生成更长的元组, 也可以使用* 号来复制出更多同样的元组.
1.1 元组拆包
如果想要将元组型的表达式复制给变量, python会对等号右边的值进行拆包:
In [15]: a = (4, 5, 6)
In [16]: x, y, z = a
In [17]: y
Out[17]: 5
嵌套拆包也是可以的:
In [18]: a = 4, 5, (6, 7)
In [19]: a
Out[19]: (4, 5, (6, 7))
使用这个功能可以轻松交换便令名. 但是再python中, 交换两个变量可以使用下面的命令完成:
In [21]: a, b = 1,2
In [22]: a
Out[22]: 1
In [23]: b
Out[23]: 2
In [24]: b, a = a, b
In [25]: a
Out[25]: 2
In [26]: b
Out[26]: 1
python有一个高级拆包功能, 用于从元组开始的位置采集一些元素. 这个功能使用特殊的语法*rest, 用于再函数调用时获取任意长度的位置参数列表:
In [29]: values = 1, 2, 3, 4, 5
In [30]: a, b, *rest = values
In [31]: a, b
Out[31]: (1, 2)
In [32]: rest
Out[32]: [3, 4, 5]
rest部分有时是向丢弃的数据, rest这个变量并没有什么特殊之处, 为了方便, 很多变成会使用下划线代替变量
In [33]: a, b, *_ = values
1.2 元组方法
由于元组的内容和长度时无法改变的, 它的实例方法很少. 一个常见的有用方法时count(列表中也可以用), 用于计量某个数值在元组中出现的次数:
In [34]: a = (1, 2, 2, 2, 3, 4, 2)
In [35]: a.count(2)
Out[35]: 4
2 列表
与元组不同, 列表的长度时可变的, 它所包含的内容也是可以修改的. 可以使用[ ]或者list类型函数来定义列表:
In [36]: a_list = [2, 3, 7, None]
In [37]: a = ('foo', 'bar', 'baz')
In [38]: b_list = list(a)
In [39]: b_list
Out[39]: ['foo', 'bar', 'baz']
In [40]: b_list[1] = 'peekaboo'
In [41]: b_list
Out[41]: ['foo', 'peekaboo', 'baz']
列表与元组相似, 他们很多用法也是相似的, 但是元组不能更改.
list函数在数据处理中常用于将迭代器或者生成器转化为列表:
In [42]: gen = range(10)
In [43]: gen
Out[43]: range(0, 10)
In [44]: list(gen)
Out[44]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2.1 增加和移除元素
使用append方法可以将元素添加到列表的尾部:
In [45]: b_list.append('dwarf')
In [46]: b_list
Out[46]: ['foo', 'peekaboo', 'baz', 'dwarf']
使用insert方法可以将元素插入到指定的列表位置:
In [47]: b_list.insert(1, 'red')
In [48]: b_list
Out[48]: ['foo', 'red', 'peekaboo', 'baz', 'dwarf']
insert 和 append相比, 计算代价更高. 因为子序列元素不得不在内部移动为新元素提供空间. 如果想要在序列的头部和尾部插入元素, 那么应该使用collections.deque, 它是一个双端队列, 可以满足头尾部都增加的要求.
insert的反向操作时pop, 该操作会将特定位置的元素移除并返回:
In [48]: b_list
Out[48]: ['foo', 'red', 'peekaboo', 'baz', 'dwarf']
In [49]: b_list.pop(2)
Out[49]: 'peekaboo'
In [50]: b_list
Out[50]: ['foo', 'red', 'baz', 'dwarf']
元素可以通过remove方法移除, 该方法会定位一个符合要求的值移除它:
In [51]: b_list.append('foo')
In [52]: b_list
Out[52]: ['foo', 'red', 'baz', 'dwarf', 'foo']
In [53]: b_list.remove('foo')
In [54]: b_list
Out[54]: ['red', 'baz', 'dwarf', 'foo']
如果不考虑性能, 通过使用append和remove, 可以将python的列表用作一种完全合适的多集和结构.
使用in关键字可以检索一个值是否在列表中:
In [55]: 'dwarf' in b_list
Out[55]: True
not 关键字可以用作in的反义词, 表示不在:
In [56]: 'dwarf' not in b_list
Out[56]: False
与字典, 集合相比, 检查列表中是否包含一个值是非常缓慢的. 这是因为python在列表中进行了线性逐个扫描, 而在字典和集合中python时同时检查所有元素的.
2.2 连接和联合列表
与元素类似, 两个列表可以使用+号来连接:
In [57]: [4, None, 'foo'] + [7, 8, (2, 3)]
Out[57]: [4, None, 'foo', 7, 8, (2, 3)]
如果有一个已经定义的列表, 可以使用extend方法向该列表添加多个元素:
In [58]: x = [4, None, 'foo']
In [59]: x.extend([7, 8, (2, 3)])
In [60]: x
Out[60]: [4, None, 'foo', 7, 8, (2, 3)]
请注意通过添加内容来连接列表是一种相对高代价的操作, 这是因为连接过程中创建了新列表,并且还要复制对象. 使用extend将元素添加到已经存在的列表时更好的方式, 尤其时在需要构建一个大型列表时:
In [61]: everything = []
In [62]: for chunk in list_of_lists:
...: everything.extend(chunk)
并且上述实现要比下面更快:
In [61]: everything = []
In [62]: for chunk in list_of_lists:
...: everything = everything + chunk
2.3 排序
可以调用列表的sort方法对列表进行内部排序(不需要新建对象):
In [63]: a = [7, 2, 5, 1, 3]
In [64]: a.sort()
In [65]: a
Out[65]: [1, 2, 3, 5, 7]
sort有一些选项偶尔会派上用场. 其中一项是传递一个二级排序key(一个生成排序值的函数). 例如, 通过字符串的长度排序:
In [66]: b = ['saw', 'small', 'He', 'foxes', 'six']
In [67]: b.sort(key=len)
In [68]: b
Out[68]: ['He', 'saw', 'six', 'small', 'foxes']
2.4 二分搜索和已排序列表的维护
内建的bisect模块实现了二分搜索和已排序列表的插值. bisect.bisect会找到元素应当被插入的位置, 并保持序列排序, 而bisect.insort将元素插入到相应位置:
In [69]: import bisect
In [70]: c = [1, 2, 2, 2, 3, 4, 7]
In [71]: bisect.bisect(c, 2)
Out[71]: 4
In [72]: bisect.bisect(c,5)
Out[72]: 6
In [73]: bisect.insort(c, 6)
In [74]: c
Out[74]: [1, 2, 2, 2, 3, 4, 6, 7]
2.5 切片
使用切片符号可以对大多数序列类型选取其子集, 它的基本形式是将start:stop传入到索引符号[ ]中:
In [75]: seq = [7, 2, 3, 7, 5, 6, 0, 1]
In [76]: seq[1:5]
Out[76]: [2, 3, 7, 5]
切片还可以将序列赋值给变量:
In [77]: seq[3:4] = [6, 3]
In [78]: seq
Out[78]: [7, 2, 3, 6, 3, 5, 6, 0, 1]
由于起始位置start的索引是包含的, 而结束位置stop的索引不包含, 因此元素的数量是stop - start.
start和stop是可以省略的, 如果省略的话会默认传入序列的起始位置或结束位置:
In [79]: seq[:5]
Out[79]: [7, 2, 3, 6, 3]
In [80]: seq[3:]
Out[80]: [6, 3, 5, 6, 0, 1]
负索引可以从序列的尾部进行索引:
In [81]: seq[-4:]
Out[81]: [5, 6, 0, 1]
In [82]: seq[-6:-2]
Out[82]: [6, 3, 5, 6]
步进值setp可以在第二个冒号后使用, 意思是每隔多少进行取值:
In [83]: seq[::2]
Out[83]: [7, 3, 3, 6, 1]
当需要对列表或元组进行翻转时, 一种很聪明的用法就是向步进取值为-1:
In [84]: seq[::-1]
Out[84]: [1, 0, 6, 5, 3, 6, 3, 2, 7]
3. 内建序列函数
3.1 enumerate
在遍历一个序列的同时追踪当前元素的索引. 一个自行实现的方法:
i = 0
for value in collection:
# 使用值做点事儿
i += 1
由于这种场景很常见, 所以python内建了enumerate函数, 返回(i, value)元组的序列, 其中value是元素的值, i 是元素的索引:
for i, value in enumerate(collection):
# 使用值做点事儿
当需要对数据建立索引时, 一种有效的模式就是使用enumerate构造一个字典, 将序列值映射到所因为值上:
In [85]: some_list = ['foo', 'bar', 'baz']
In [86]: mapping = {}
In [87]: for i, v in enumerate(some_list):
...: mapping[v] = i
In [89]: mapping
Out[89]: {'foo': 0, 'bar': 1, 'baz': 2}
3.2 sorted
sorted函数返回一个根据任意序列中的元素新建的已排序列表:
In [90]: sorted([7, 1, 2, 6, 0, 3, 2])
Out[90]: [0, 1, 2, 2, 3, 6, 7]
sorted函数接受的参数与列表的sort方法一致.
3.3 zip
zip将列表, 元组或其它序列的元素配对, 新建一个元组构成的列表:
In [91]: seq1 = ['foo', 'bar', 'baz']
In [92]: seq2 = ['one' , 'two', 'three']
In [93]: zipped = zip(seq1, seq2)
In [94]: list(zipped)
Out[94]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip可以处理任意长度的序列, 它生成列表长度由最短的序列决定:
In [95]: seq3 = [False, True]
In [96]: list(zip(seq1, seq2, seq3))
Out[96]: [('foo', 'one', False), ('bar', 'two', True)]
zip的常用场景为同时遍历多个序列, 有时候会和enumerate同时使用:
In [97]: for i, (a, b) in enumerate(zip(seq1, seq2)):
...: print('{0}: {1}, {2}'.format(i, a, b))
...:
0: foo, one
1: bar, two
2: baz, three
给定一个已经配对的序列时, zip函数有一种机智的方式区拆分序列. 这种方法的另一种思路就是将行的列表转化为列的列表. 语法上看上去很魔幻:
In [98]: pitcters = [('Nolan', 'Ryan'), ('Roger', 'Clemens'), ('Schilling', 'Cur
...: t')]
In [100]: first_names, last_names = zip(*pitcters)
In [101]: first_names
Out[101]: ('Nolan', 'Roger', 'Schilling')
In [102]: last_names
Out[102]: ('Ryan', 'Clemens', 'Curt')
3.4 reversed
reversed函数将序列的元素倒序排列:
In [103]: list(reversed(range(10)))
Out[103]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
未完待续。。。