# -*- coding:utf-8 -*-
# __author__ = 'lipzhang'
#第2章 序列构成的数组
#2.1 内置序列类型概览
# 容器序列
# list、tuple 和 collections.deque 这些序列能存放不同类型的数据。
# 扁平序列
# str、bytes、bytearray、memoryview 和 array.array,这类序列只能容纳一种类型。
#容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是 引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧 凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。
# 可变序列
# list、bytearray、array.array、collections.deque 和 memoryview。
# 不可变序列
# tuple、str 和 bytes。
# 2.2 列表推导和生成器表达式
# 列表推导是构建列表(list)的快捷方式,而生成器表达式则可以用来创建其他任何类型 的序列。如果你的代码里并不经常使用它们,那么很可能你错过了许多写出可读性更好且 更高效的代码的机会。
# 2.2.1 列表推导和可读性
symbols = '$¢£¥€¤'
codes = [ord(symbol) for symbol in symbols]
print(codes)
# 2.2.4 生成器表达式
#生成器表达式的语法跟列表推导差不多,只不过把方括号换成圆括号而已。
#虽然也可以用列表推导来初始化元组、数组或其他序列类型,但是生成器表达式是更好的 选择。这是因为生成器表达式背后遵守了迭代器协议,可以逐个地产出元素,而不是先建 立一个完整的列表,然后再把这个列表传递到某个构造函数里。前面那种方式显然能够节 省内存。
# 2.3.4 具名元组 collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有 名字的类——这个带名字的类对调试程序有很大帮助。
# 用 namedtuple 构建的类的实例所消耗的内存跟元组是一样的,因为字段名都 被存在对应的类里面。这个实例跟普通的对象实例比起来也要小一些,因为 Python 不会用 __dict__ 来存放这些实例的属性。
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
print(tokyo)
print(tokyo.population)
# 创建一个具名元组需要两个参数,一个是类名,另一个是类的各个字段的名字。后者可 以是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串。
# 序列构成的数组 | 27
# ➋ 存放在对应字段里的数据要以一串参数的形式传入到构造函数中(注意,元组的构造函 数却只接受单一的可迭代对象)。 ➌ 你可以通过字段名或者位置来获取一个字段的信息。
# 除了从普通元组那里继承来的属性之外,具名元组还有一些自己专有的属性。示例 2-10 中 就展示了几个最有用的:_fields 类属性、类方法 _make(iterable) 和实例方法 _asdict()。
print(City._fields)#_fields 属性是一个包含这个类所有字段名称的元组。
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data)#用 _make() 通过接受一个可迭代对象来生成这个类的一个实例,它的作用跟 City(*delhi_data) 是一样的。
print(delhi._asdict())#_asdict() 把具名元组以 collections.OrderedDict 的形式返回,我们可以利用它来把元 组里的信息友好地呈现出来。
# 2.3.5 作为不可变列表的元组
#如果要把元组当作列表来用的话,最好先了解一下它们的相似度如何。
# 在表 2-1 中可以清 楚地看到,除了跟增减元素相关的方法之外,元组支持列表的其他所有方法。
# 还有一个例 外,元组没有 __reversed__ 方法,但是这个方法只是个优化而已,reversed(my_tuple) 这个用法在没有 __reversed__ 的情况下也是合法的。
#表2-1:列表或元组的方法和属性(那些由object类支持的方法没有列出来)
# 列表 元组
# s.__add__(s2) • • s + s2,拼接
# s.__iadd__(s2) • s += s2,就地拼接
# s.append(e) • 在尾部添加一个新元素
# s.clear() • 删除所有元素
# s.__contains__(e) • • s 是否包含 e
# s.copy() • 列表的浅复制
# s.count(e) • • e 在 s 中出现的次数
# s.__delitem__(p) • 把位于 p 的元素删除
# s.extend(it) • 把可迭代对象 it 追加给 s
# s.__getitem__(p) • • s[p],获取位置 p 的元素
# s.__getnewargs__() • 在 pickle 中支持更加优化的序列化
# s.index(e) • • 在 s 中找到元素 e 第一次出现的位置
# s.insert(p, e) • 在位置 p 之前插入元素 e
# s.__iter__() • • 获取 s 的迭代器
# s.__len__() • • len(s),元素的数量
# s.__mul__(n) • • s * n,n 个 s 的重复拼接
# s.__imul__(n) • s *= n,就地重复拼接
# s.__rmul__(n) • • n * s,反向拼接 *
# s.pop([p]) • 删除最后或者是(可选的)位于 p 的元素,并返回它的值
# s.remove(e) • 删除 s 中的第一次出现的 e
# s.reverse() • 就地把 s 的元素倒序排列
# s.__reversed__() • 返回 s 的倒序迭代器
# s.__setitem__(p, e)• s[p] = e,把元素 e 放在位置 p,替代已经在那个位置的元素
# s.sort([key], [reverse]) • 就地对 s 中的元素进行排序,可选的参数有键(key)和是 否倒序(reverse)
#2.4 切片
# 2.4.2 对对象进行切片
#一个众所周知的秘密是,我们还可以用 s[a:b:c] 的形式对 s 在 a 和 b 之间以 c 为间隔取 值。c 的值还可以为负,负值意味着反向取值
s = 'bicycle'
print(s[::3])
print(s[::-1])
print(s[::-2])
#a:b:c 这种用法只能作为索引或者下标用在 [] 中来返回一个切片对象:slice(a, b, c)。 在 10.4.1 节中会讲到,对seq[start:stop:step] 进行求值的时候,Python 会调用seq. __getitem__(slice(start, stop, step))。就算你还不会自定义序列类型,了解一下切片对 象也是有好处的。例如你可以给切片命名,就像电子表格软件里给单元格区域取名字一样
invoice = """0.....6................................40........52...55........
1909 Pimoroni PiBrella $17.50 3 $52.50
1489 6mm Tactile Switch x20 $4.95 2 $9.90
1510 Panavise Jr. - PV-201 $28.00 1 $28.00
1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95
"""
SKU = slice(0, 6)
DESCRIPTION = slice(6, 40)
UNIT_PRICE = slice(40, 52)
QUANTITY = slice(52, 55)
ITEM_TOTAL = slice(55, None)
line_items = invoice.split('\n')[2:]
for item in line_items:
print(item[UNIT_PRICE], item[DESCRIPTION])
#2.4.3 多维切片和省略
#[] 运算符里还可以使用以逗号分开的多个索引或者是切片,外部库 NumPy 里就用到了这 个特性,二维的 numpy.ndarray 就可以用 a[i, j] 这种形式来获取,抑或是用 a[m:n, k:l] 的方式来得到二维切片。
#对象的特殊方法 __getitem__ 和 __setitem__ 需要以元组的形式来接收 a[i, j] 中的索 引。也就是说,如果要得到 a[i, j] 的值,Python 会调用 a.__getitem__((i, j))。
#省略(ellipsis)的正确书写方法是三个英语句号(...),而不是Unicdoe 码位U+2026 表 示的半个省略号(...)。省略在 Python 解析器眼里是一个符号,而实际上它是 Ellipsis 对 象的别名,而 Ellipsis 对象又是 ellipsis 类的单一实例。2 它可以当作切片规范的一部分, 也可以用在函数的参数清单中,比如 f(a, ..., z),或 a[i:...]。在 NumPy 中,... 用作 多维数组切片的快捷方式。如果 x 是四维数组,那么 x[i, ...] 就是 x[i, :, :, :] 的缩 写。
#2.4.4 给切片赋值
l = list(range(10))
l[2:5] = [20, 30]
print(l)
#2.5对序列使用+和*
#2.7 list.sort方法和内置函数sorted
#list.sort 方法会就地排序列表,也就是说不会把原列表复制一份。这也是这个方法的返 回值是 None 的原因,提醒你本方法不会新建一个列表。
#与 list.sort 相反的是内置函数 sorted,它会新建一个列表作为返回值。这个方法可以接 受任何形式的可迭代对象作为参数,甚至包括不可变序列或生成器(见第 14 章)。而不管 sorted 接受的是怎样的参数,它最后都会返回一个列表。
#2.8 用bisect来管理已排序的序列
# 2.9.1 数组
#NumPy和SciPy
#凭借着 NumPy 和 SciPy 提供的高阶数组和矩阵操作,Python 成为科学计算应用的主流语 言。NumPy 实现了多维同质数组(homogeneous array)和矩阵,这些数据结构不但能处理 数字,还能存放其他由用户定义的记录。通过 NumPy,用户能对这些数据结构里的元素进 行高效的操作。
#SciPy 是基于 NumPy 的另一个库,它提供了很多跟科学计算有关的算法,专为线性代数、 数值积分和统计学而设计。SciPy 的高效和可靠性归功于其背后的 C 和 Fortran 代码,而这 些跟计算有关的部分都源自于 Netlib 库(http://www.netlib.org)。换句话说,SciPy 把基于 C 和 Fortran 的工业级数学计算功能用交互式且高度抽象的 Python 包装起来,让科学家如 鱼得水。
#。NumPy 和 SciPy 都是异常强大的库,也是其他一些很有用的 工具的基石。Pandas(http://pandas.pydata.org)和 Blaze(http://blaze.pydata.org)数据分析 库就以它们为基础,提供了高效的且能存储非数值类数据的数组类型,和读写常见数据文 件格式(例如 csv、xls、SQL 转储和 HDF5)的功能。因此,要详细介绍 NumPy 和 SciPy 的话,不写成几本书是不可能的。虽然本书不在此列,但是如果要对 Python 的序列类型做 一个概览,恐怕没有人能忽略 NumPy。
#2.9.4 双向队列和其他形式的队列
#collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的 数据类型。而且如果想要有一种数据类型来存放“最近用到的几个元素”,deque 也是一个 很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这 个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素。
from collections import deque
dq = deque(range(10), maxlen=10)#maxlen 是一个可选参数,代表这个队列可以容纳的元素的数量,而且一旦设定,这个 属性就不能修改了。
print(dq)
dq.rotate(3)#队列的旋转操作接受一个参数 n,当 n > 0 时,队列的最右边的 n 个元素会被移动到队 列的左边。当 n < 0 时,最左边的 n 个元素会被移动到右边。
print(dq)
dq.appendleft(-1)#当试图对一个已满(len(d) == d.maxlen)的队列做尾部添加操作的时候,它头部的元 素会被删除掉。注意在下一行里,元素 0 被删除了。
print(print(dq))
dq.extend([11, 22, 33])#在尾部添加 3 个元素的操作会挤掉 -1、1 和 2。
print(dq)
dq.extendleft([10, 20, 30, 40])# extendleft(iter) 方法会把迭代器里的元素逐个添加到双向队列的左边,因此迭代器里 的元素会逆序出现在队列里。
print(dq)
# #queue 提供了同步(线程安全)类 Queue、LifoQueue 和 PriorityQueue,不同的线程可以利用 这些数据类型来交换信息。这三个类的构造方法都有一个可选参数 maxsize,它接收正 整数作为输入值,用来限定队列的大小。但是在满员的时候,这些类不会扔掉旧的元素 来腾出位置。相反,如果队列满了,它就会被锁住,直到另外的线程移除了某个元素而 腾出了位置。这一特性让这些类很适合用来控制活跃线程的数量。
# multiprocessing
# 这个包实现了自己的 Queue,它跟 queue.Queue 类似,是设计给进程间通信用的。同时 还有一个专门的 multiprocessing.JoinableQueue 类型,可以让任务管理变得更方便。
# asyncio
# Python 3.4 新提供的包,里面有Queue、LifoQueue、PriorityQueue 和 JoinableQueue, 这些类受到 queue 和 multiprocessing 模块的影响,但是为异步编程里的任务管理提供 了专门的便利。
# heapq
# 跟上面三个模块不同的是,heapq 没有队列类,而是提供了 heappush 和 heappop 方法, 让用户可以把可变序列当作堆队列或者优先队列来使用。