本章讨论的内容是序列类型
2.1 内置序列类型概览
根据存储数据类型分类
容器序列: 能存放不同类型的数据
list, tuple, collections.deque
扁平序列:只能容纳一种类型
str, bytes, byte array, memoryview, array.array
根据是否被修改分类
可变类型
list, byte array, array.array, collections.deque, memoryview
不可变类型
tuple, str, bytes
2.2 列表推导和生成器表达式
2.2.1 列表推导和可读性
# 字符串变成unicode码
symbols = '$¢£¥€¤'
codes = []
for symbol in symbols:
codes.append(ord(symbol))
# 列表推导式写法
codes = [ord(symbol) for symbol in symbols]
NOTE:列表推导不会再有变量泄漏的问题
2.2.2 列表推导同filter和map的比较
symbols = '$¢£¥€¤'
# 列表推导
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
# map/filter
beyond_ascii = list(filter(lambda c: c>127, map(ord, symbols)))
2.2.3 笛卡尔积
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes]
2.2.4 生成器表达式
生成器表达式的语法跟列表推导差不多,只不过是方括号换成圆括号而已
symbols = '$¢£¥€¤'
# 生成器推导
beyond_ascii = (ord(s) for s in symbols if ord(s) > 127)
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = ((color, size) for color in colors for size in sizes)
2.3 元组不仅仅是不可变的列表
2.3.1 元组和记录
元组可以对数据记录,每个元素记录了字段的数据
city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)
2.3.2 元组拆包
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates # 元组拆包
# 用*来处理剩下的元素
a, b, *rest=range(5)
关于拆包,可以参考可迭代元素拆包
2.3.3 嵌套元组拆包
metro_areas = [
('Tokyo','JP',36.933,(35.689722,139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333))
]
# 嵌套元组拆包
for name, cc, pop, (latitude, longitude) in metro_areas:
if longitude <= 0:
print(fmt.format(name, latitude, longitude))
2.3.4 具名元组
collections.namedtuple
: 用来构建只有少数属性但是没有方法的对象
_fields
属性:包含这个类所有字段名称的元组
_make
: 接收可迭代对象生成类实例,作用类似City(*delhi_data)
_asdict
: 具名元组以collections.OrderedDict
形式返回
>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
>>> City._fields
>>> LatLong=namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data)
>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
2.3.5 作为不可变列表的元组
较之list, 除了增减元素相关的方法以外,元组支持列表的其他所有方法
2.4 切片
2.4.1 为什么切片和区间会忽略最后一个元素
- 通过最后一个位置信息,快速看出切片和区间有几个元素:range(3)和my_list[:3]都返回3个元素
- 当起止位置信息都可见时,快速计算切片和区间的长度,下标(stop-start)即可
- my_list[:x]和my_list[x:] 分割成不重叠的两部分
2.4.2 对对象进行切片
>>> s = 'bicycle'
>>> s[::3]
'bye'
2.4.3 多维切片和省略
多维切片:见2.9.3 Numpy
省略:Ellipsis
2.4.4 给切片赋值
>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[2:5] = [20, 30]
>>> l
[0, 1, 20, 30, 5, 6, 7, 8, 9]
>>> del l[5:7]
>>> l
[0, 1, 20, 30, 5, 8, 9]
>>> l[3::2] = [11, 22]
>>> l
[0, 1, 20, 11, 5, 22, 9]
# 如果赋值的对象时一个切片,那么赋值语句右侧必须是可迭代对象
>>> l[2:5] = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
>>> l[2:5] = [100]
>>> l
[0, 1, 100, 22, 9]
2.5 对序列使用+和*
+和* 不修改原有的操作对象,而是构建一个全新的序列
>>> board = [['_'] * 3 for i in range(3)]
>>> board
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] >>> board[1][2] = 'X'
>>> board
[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
下列是经常遇到的错误,对同一对象的引用的列表
>>> weird_board = [['_'] * 3] * 3
>>> weird_board
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] >>> weird_board[1][2] = 'O'
>>> weird_board
[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]
2.6 序列的增量赋值
>>> a += b
在这个表达式中,变量名会不会关联到新的对象,取决于__iadd__
方法是否实现
- 如果a实现了
__iadd__
方法,就调用这个方法。对可变序列(例如list, bytearray, array.array)来说, a会就地改动,就像调用了a.extend(b) - 如果没有实现
__iadd__
方法,a+=b这个表达式的效果就等效a=a+b;首先计算a+b,得到新的对象,然后赋值给a
>>> l = [1, 2, 3]
>>> id(l) 4311953800
>>> l *= 2
>>> l
[1, 2, 3, 1, 2, 3]
>>> id(l) 4311953800
>>> t = (1, 2, 3)
>>> id(t) 4312681568
>>> t *= 2
>>> id(t) 4301348296
str 不适用上述规则,因为对字符串+=太普遍,CPython对它做了优化,为str初始化内存的时候,程序预留了额外了可扩展空间,增量操作,不会涉及复制原有字符到新位置的操作。
2.7 list.sort方法和内置函数sorted
Python 的一个惯例:如果一个函数或者方法对对象进行的是就地改动,那它就应该返回None,好让调用者知道传入的参数发生了变动,而且并未产生新的对象
list.sort: 就地排序列表,不会新建列表
sorted:新建列表作为返回值
>>> fruits = ['grape', 'raspberry', 'apple', 'banana']
>>> sorted(fruits)
['apple', 'banana', 'grape', 'raspberry']
>>> fruits
['grape', 'raspberry', 'apple', 'banana']
>>> fruits.sort()
>>> fruits
['apple', 'banana', 'grape', 'raspberry']
2.8 用bisect来管理已排序的序列
2.8.1 用bisect来搜索
分为bisect.bisect (bisect_right别名), bisect.bisect_left
import bisect
import sys
HAYSTACK=[1,4,5,23,23,26,29,30]
postion=bisect.bisect(HAYSTACK, 23)
print(postion)
postion=bisect.bisect_left(HAYSTACK, 23)
print(postion)
#结果:
5
3
2.8.2 用bisect.insort插入新元素
import bisect
import sys
my_list=[1,4,5,26,29,30]
bisect.insort(my_list, 23)
print(my_list)
# 结果
[1, 4, 5, 23, 26, 29, 30]
2.9 当列表不是首选时
2.9.1 数组
一个浮点型数据的创建,存入文件和从文件读取的过程
from array import array
from random import random
floats=array('d', (random() for i in range(10**7)))
floats[-1]
# 0.10839796787230571
fp=open('floats.bin', 'wb')
floats.tofile(fp)
fp.close()
floats2=array('d')
fp=open('floats.bin', 'rb')
floats2.fromfile(fp, 10**7)
fp.close()
floats2[-1]
# 0.10839796787230571
floats2==floats
# True
2.9.2 内存视图
通过改变数组中的一个字节来更新数组里某个元素的值
import array
numbers=array.array('h', [-2, -1, 0, 1, 2])
memv=memoryview(numbers)
len(memv)
# 5
memv[0]
# -2
memv_oct=memv.cast('B')
memv_oct.tolist()
# [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
2.9.3 NumPy和SciPy
对numpy.ndarray的行和列进行基本操作
import numpy
a=numpy.arrange(12)
# array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
a.shape=3,4
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
2.9.4 双向队列和其他形式的队列
-
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.extend([11, 22, 33]) dq # deque([0, 1, 2, 3, 4, 5, 6, 11, 22, 33], maxlen=10)
-
queue:
线程安全类Queue, LifoQueue, PriorityQueue
-
multiprocessing:
Queue设计给进程通信用的
multiprocessing.JoinableQueue类型为任务管理提供便利
-
asyncio:
Queue, LifoQueue, PriorityQueue, JoinableQueue,受queue和multiprocessing模块影响,为异步编程的任务管理提供便利
-
heapq:
没有队列类,提供了heappush和heappop方法,可以把可变队列当作堆队列或优先队列使用