序列数据类型
分类
- 容器序列:list、tuple、collections.deque,包含任何类型的引用
- 扁平序列:str、bytes、bytearray、memoryview和array.array,存放的是值而不是引用,只能容纳一种类型
- 可变序列:list、bytearray、array.array、collections.deque和memoryview
- 不可变序列:tuple、str和bytes
列表推倒和生成器表达式
列表推导常用来创建列表
[s for s in symbols if s != 'a']
生成器用来创建数组、元组等其他序列类型,虽然列表推导也能实现,但序列推导是先创建序列再转成其他类型,而生成器表达式是利用迭代的方法逐个产生元素
for i in ('%s %s' % (x, y) for x in rows for y in cols)
生成器优点:它不是一次产生这10000种笛卡尔积组合,而是一个一个产生,因此在上式for循环中内存占用要小很多,速度也要更快,而且它可以生成列表以外的序列,工作原理请看第14章节
元组
有人叫元组为“不可变列表”,但其实元组更像某些数据的集合(看起来很像map),这样更能体现出元组拆包的特性
tips:
-
*t可以把一个可迭代对象t拆开作为函数的参数
-
占位符_可以在拆包时帮忙处理元组中不感兴趣的数据
-
*agrs可以获取不确定数量的参数,最后得到的是一个list
a,b,*rest=range(5) 0,1,[2,3,4]
具名元组namedtuple
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
tokyo.coordinates
具名元组更像map,但是格式完全不一样,其实应该理解成一个只有数据没有方法的类。
创建namedtuple需要两个参数,第一个是类名,第二个是字段名,可以是空格分割的字符串,也可以是字符串组成的可迭代对象。
tips:
- City._fields是一个包含City所有字段名的元组
- City._make(Obj)是用一个可迭代对象Obj来生成City的实例,等同于City(*Obj)
- City._asdict()是输出命名元组的方法
切片
s[start: stop :step]——取不到stop,所以len=stop-start,step可以为负数,那样就是从stop开始反着取,这样取不到start
切片对象slice(start, stop, step),在需要复用切片对象的时候很好用。
给切片赋值:注意一点,等号右边必须是一个可迭代对象,如[100]
I[2:5] = [20,30]
+和*:可用于所有的序列,+是把左右两个拼接起来,*是把该序列的元素复制n回
['_' * 3] * 3 = [['_', '_', '_'],['_', '_', '_'],['_', '_', '_']],但错误,因为它是把['_', '_', '_']的引用复制了3次,所以这三个其实是一个东西
改正就是用fori in range(3)每次新建引用
['_'] * 3 = ['_', '_', '_']是正确的,因为字符串是不可变对象,它是值不是引用
sort
list.sort()返回None,对list就地改动;sorted()返回一个新list
参数
- reverse: True降序,False升序
- key: 只有一个参数的函数
排序是稳定的
bisect和insort
bisect(haystack, needle)——在干草堆中找针的位置,返回index
haystack.insert(index, needle)——在index处插入新值
insort(seq, item)——在seq中插入item,一步到位且速度更快
列表的更优解
数组 array.array
当我们需要一个只包含数字的列表时,array比list更高效,因为它背后不是float对象,而是数字的机器翻译,和C语言中数组一样。
它是可变列表,所以它支持pop、insert、extend,以及文件操作tofile、fromfile,速度更快。
创建
from array import array
floats = array('d', (ramdom() for i in range(10**7)))
# 'd'是double类型,可迭代对象
文件
floats.tofile(open('floats.bin', 'wb'))
floats2 = array('d')
floats2.fromfile(open('floats.bin', 'rb'), 10**7)
# 写入二进制文件,按double格式读出10^7个数据
排序
a=array(a.typecode, sorted(a))
# 数组不在支持就地排序,只能新建一个数组
另一个快速序列化数字的方法是pickle模块,而且它可以处理几乎所有的内置数据类型,包含复数、嵌套集合,甚至用户自定义的类等,它的速度和array.tofile一样快
内存视图 memoryview
memoryview可以和array共享一块内存空间,然后memoryview.cast可以用不同的方式看待数据,和C语言中类型转换的概念差不多。
number = array.array('h', [-2,-1,0,1,2]) # 有符号字符
memv = memoryview(number)
memv_oct = memv.cast('B') # 无符号整数
# memv_oct.tolist() = [254,255,255,255,0,0,1,0,2,0]
memv_oct[5] = 4
# number = array('h', [-2,-1,1024,1,2])
NumPy和SciPy
很好做高维数组和矩阵的建立,以及相关的线性代数、数学分析等运算公式,详细请看其他文章
队列
collections.deque
它是一个线程安全,可以快速从两端头尾添加或删除元素的队列,但对中间元素操作较为缓慢。
dq = deque(range(10), maxlen=10)
# 可迭代对象,maxlen是可选参数,一旦设定不能修改
如果插入后超过maxlen,会自动从另一端顶出去一个元素
queue
它提供了同步(线程安全)的类Queue、LifoQueue和PriorityQueue,用于线程间交换信息。
不同于collection.deque的点在于Queue等在满员后会被锁住,知道有其他线程移除某元素,所以它可以用作任务管理,也可以用作生产者-消费者问题中的同步缓冲区
multiprocessing
它实现了自己的Queue,也是用于线程通信的,还有一个专门的multiprocessing.JoinableQueue类型,使任务管理更方便。
asyncio
Python 3.4 新提供的包,里面有 Queue、LifoQueue、PriorityQueue 和 JoinableQueue
heapq
它没有队列类,而是提供了heappush和heappop操作,把可变序列看作堆队列或者优先队列使用。