Python中的序列


内置序列类型概述:

Python标准库使用C实现了丰富的序列类型,列举如下:

  • 容器序列
    listtuplecollsctions.deque这些序列能存放不同的数据类型。
  • 扁平序列
    str,bytes,bytearray,memoryview和array.array,这些序列只能存放一种类型。

容器序列存放的是他们所包含的任意类型的对象引用,而扁平序列存放的是值而不是引用,也就是说,扁平序列其实是一段连续的内存空间,因此扁平序列其实更加紧凑,但是他们里面只能存放例如字符,字节和数值这种基础类型。
序列类型还可以按照是否可以修改赖分类。

  • 可变序列
    listbytearrayarray.array,collections.dequememview
  • 不可变序列
    tuplestrbytes

列表推导和生成器表达式

虽然都可以使用列表推导赖初始化元组,数组或其他序列类型,但是生成器表达式是更好都选择,因为生成器表达式背后遵循了迭代器都协议,可以逐个的产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数中。 下面分别是列表推导和生成器表达式的简单示例:

>>>vals=range(10)
>>>[s for s in vals if s>5]
[6, 7, 8, 9]
>>>tuple(s for s in vals if s>5)
(6, 7, 8, 9)

元组

元组其实是对数据的记录,元组的每个元素都存放了记录中都一个字段都数据,外加这个字段的位置.其实元组还有很多用法,其中一种就是可以用*操作符把一个可迭代对象拆开作为函数的参数:

>>> divmod(20,8)
(2, 4)
>>> t=(20,8)
>>> divmod(*t)
(2, 4)
>>> quotient,remainder=divmod(*t)
>>> quotient,remainder
(2, 4)

另外_操作符可以帮我们丢弃不需要的东西,比如:

>>> import os
>>> _,filename=os.path.split('/home/pi/.ssh/idrsa.pub')
>>> filename
'idrsa.pub'

还可以用*来处理剩下的元素:

>>> a,b,*rest=range(5)
>>> a,b,rest
(0, 1, [2, 3, 4])
>>> a,b,*rest=range(3)
>>> a,b,rest
(0, 1, [2])
>>> a,b,*rest=range(2)
>>> a,b,rest
(0, 1, [])

具名元组

colections.namedtuple是一个工厂函数,他可以用来构建一个带字段名带元组和一个有名字的类。如下所示:

>>> from collections import namedtuple
>>> City=namedtuple('City','name country population coordinates')
>>> tokyo=City('Tokyo','JP','36',(35.689,139.691))
>>> tokyo
City(name='Tokyo', country='JP', population='36', coordinates=(35.689, 139.691))
>>> tokyo.population
'36'
>>> tokyo.coordinates
(35.689, 139.691)
>>> tokyo[1]
'JP'

构造函数的参数分别是类名,另一个是类的各个字段的名字,可以是数个字符串组成的可迭代对象,或者是空格分开的字段名组成的字符串,实例化的时候,字段的参数要以一串参数的形式传入。

其他列表类型

数组

如果要存放大量的数字或浮点数,那么数组的效率就要高的多,这是因为数组再背后存放的不是float对象,而是数字的机器翻译,也就是字节表述,这点和c语言的数组一样。创建数组需要一个类型码,这个类型码用来表示再底层的c语言应该存放怎样的数据类型。下面展示了其简单的用法:

>>> from array import array
>>> from random import random
>>> floats=array('d',(random() for i in range(10**7)))
>>> floats[-1]
0.7779414006542579
>>> fp=open('floats.bin','wb')
>>> floats.tofile(fp)
>>> fp.close()
>>> floats2=array('b')
>>> fp=open('floats.bin','rb')
>>> floats2.fromfile(fp,10**7)
>>> fp.close()
>>> floats2[-1]
0.7779414006542579
>>> floats[2]==floats
True

内存视图

memoryview是一个内置类,可以让用户再不复制内容的情况下操作同一个数组的不同切片。memoryview.cast能用不同的方式读写同一块内存数据,而且内容字节不会随意移动。有点类似于c语言的类型转换。

>>> numbers=array('h',[-2,-1,0,1,2])
>>> memv=memoryview(numbers)
>>> len(memv)
5
>>> memv[0]
-2
>>> memv_oct=memv.cast('B') #转换为无符号整形
>>> memv_oct[0]
254
>>> memv_oct.tolist()
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
>>> memv_oct[5]=4  #把第五个位置的字节改为4
>>> numbers
array('h', [-2, -1, 1024, 1, 2])

双向队列和其他队列

在把列表当作队列或者栈使用时(pop或append),有时候这种操作是很耗时的,因为牵扯到移动列表的所有元素,collections.deque(双向队列)是一颗线程安全,可以迅速从两端添加或者删除元素的数据类型。

>>> from collections import deque
>>> dq=deque(range(10),maxlen=10) #队列容纳元素最大值,不可修改
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3) #旋转操作
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1)
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11,22,33])
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
>>> dq.extendleft([10,20,30,40])
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)