Table of Contents
Chapter 2 序列构成的数组
列表
分类
从存放的数据类型可以分为:
- 容器序列(container sequence),如:
list
、tuple
、collections.deque
,可以存放不同类型数据的*引用*。 - 扁平序列(flat sequence),如:
str
、bytes
、bytearray
、memoryview
和array.array
, 只能容纳字符、字节和数值这种基础类型。扁平序列是一段连续的内存空间,更加紧凑。
从是否可变可以分为:
- 可变序列,如:
list
、bytearray
、array.array
、collections.deque
和memoryview
。 - 不可变序列,如:
tuple
、str
和bytes
。
列表推导
通常的原则是,列表推导只用来创建新的列表,并保持简短,尽量避免滥用,如通过列表推导重复获取列表的副作用。
*列表推导不会再有变量泄漏的问题*,在 Python 2.x
中,列表推导 for
关键字之后的赋值可能会影响上下文中的同名变量
>>> x = 'my precious' >>> dummy = [x for x in 'ABC'] >>> x 'C'
但在 Python 3
中列表推导有了自己的局部作用域,不会影响上下文的赋值。
元组(tuple)
元组拆包或可迭代元素拆包(Iterable Unpacking)
- 交换两个变量的值:
a, b = b, a
- 用 * 运算符把一个可迭代对象拆开作为函数的参数:
test(*a_tuple)
- 用*来处理剩下的元素:
a, b, *rest = range(5)
namedtuple
用 namedtuple
构建的类,内存消耗跟元组一样,比普通的对象实例消耗要小一些,常用方法or属性: _fields
、 _make()
、 _asdict()
。
作为不可变列表
除增减元素相关方法外,元素支持列表的其他所有方法,另外元组没有 _reversed__
方法,不过可以通过 reverse(a_tup)
替代。
切片
如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象。即便只有单独 一个值,也要把它转换成可迭代的序列。
对序列使用 + 和 *
对序列使用 + 和 *,都不会改变原有序列,而是重新创建新的序列。但如果序列中的元素是对象的引用,则需要注意下面这种情况:
>>> weird_board = [['_'] * 3] * 3 >>> weird_board [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] >>> weird_board[1][2] = 'O' >>> weird_board [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]
weird_board
中的三个元素都是对同一个列表的引用,所以在赋值时,其实是在操作同一个列表,所以要通过每次创建新的列表来避免这种问题, board = [['_'] * 3 for i in range(3)]
。
序列的增量赋值
增量赋值运算符 += 和 *= 的表现取决于它们的第一个操作对象。
- += 背后的特殊方法是
__iadd__
(用于“就地加法”),会赋值给原对象,没有__iadd__
方法会退一步调用__add__
方法,会产生一个新的对象,并把结果赋值给新对象。 - *= 作用在可变序列上时会在原序列上追加新元素;作用在不可变序列上时,每次都会产生新对象,效率极低。
str
是一个例外,因为对字符串做 += 实在是太普遍了,所以 CPython 对它做了优化。为 str 初始化内存的时候,程序会为它留出额外的可扩展空间,因此进行增量操作的时候,并不会涉及复制原有字符串到新位置这类操作。
一个关于+=的谜题
>>> t = (1, 2, [30, 40]) >>> t[2] += [50, 60] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> t (1, 2, [30, 40, 50, 60])
由于 t[2]
是可变的,所以列表元素正常追加,但将结果赋值给 t[2]
就会报错,因为t是元素,不可变。
所以:
- 不要把可变对象放在元组里面
- 增量赋值不是一个原子操作
bisect
根据分数,找到对应传成绩等级
def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): i = bisect.bisect(breakpoints, score) return grades[i]
当列表不再是首选
- 只处理数字列表时,
array.array
比list
更高效 - memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片
- collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型
- queue
- multiprocessing
- asyncio
- heapq