python进阶书目串烧(四)

内存视图1

memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片。memoryview 的概念受到了 NumPy 的启发(参见2.9.3 节)。Travis Oliphant 是 NumPy 的主要作者,他在stackoverflow回答“ Whenshould a memoryview beused?”中对memoryview 的功能做了说明2

memoryview.cast 的概念跟数组模块类似,能用不同的方式读写同一块内存数据,而且内容字节不会随意移动。这听上去又跟 C 语言中类型转换的概念差不多。memoryview.cast 会把同一块内存里的内容打包成一个全新的 memoryview 对象给你。

通过改变数组中的一个字节来更新数组里某个元素的值:

numbers = array('h', [-2, -1, 0, 1, 2])
memv = memoryview(numbers)  # 利用含有5个短整型有符号整数的数组创建一个memoryview。
print(len(memv))  # 5
print(memv[0])  # -2
memv_oct = memv.cast('B')
print(memv_oct.tolist())  # [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
memv_oct[5] = 4
print(numbers)  # array('h', [-2, -1, 1024, 1, 2])

NumPy3和SciPy

凭借着 NumPy 和 SciPy4 提供的高阶数组和矩阵操作,Python 成为科学计算应用的主流语言。

numpy.ndarray 的行和列进行基本操作:

import numpy

a = numpy.arange(12)
print(a)  # array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11])
print(type(a))  # <class 'numpy.ndarray'>
print(a.shape)  # (12,)
a.shape = 3, 4
print(a)
# array([[  0,  1,  2,  3],
#        [  4,  5,  6,  7],
#        [  8,  9, 10, 11]])
print(a[2])  # array([  8,  9,  10,  11])
print(a[2, 1])  # 9
print(a[:, 1])  # array([1, 5, 9])
print(a.transpose())
# array([[  0,  4,  8],
#        [  1,  5,  9],
#        [  2,  6, 10],
#        [  3,  7, 11]])

NumPy 也可以对numpy.ndarray中的元素进行抽象的读取、保存和其他操作:

import numpy

floats = numpy.loadtxt('floats-10M-lines.txt')
print(floats[-3:])
array([3016362.69195522, 535281.10514262, 4566560.44373946])
floats *= .5
print(floats[-3:])
array([1508181.34597761, 267640.55257131, 2283280.22186973])
from time import perf_counter as pc

t0 = pc()
floats /= 3
print(pc() - t0)  # 0.03690556302899495
numpy.save('floats-10M', floats)
floats2 = numpy.load('floats-10M.npy', 'r+')
floats2 *= 6
print(floats2[-3:])  # memmap([3016362.69195522, 535281.10514262, 4566560.44373946])

双向队列5和其他形式的队列

利用 .append 和 .pop 方法,我们可以把列表当作栈或者队列来用(比如,把 .append.pop(0) 合起来用,就能模拟栈的“先进先出”的特点)。但是删除列表的第一个元素(抑或是在第一个元素之前添加一个元素)之类的操作是很耗时的,因为这些操作会牵扯到移动列表里的所有元素。

collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型。而且如果想要有一种数据类型来存放“最近用到的几个元素”,deque 也是一个很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素。

from collections import deque

dq = deque(range(10), maxlen=10)
print(dq)  # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.rotate(3)
print(dq)  # deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(-4)
print(dq)  # deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
dq.appendleft(-1)
print(dq)  # deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.extend([11, 22, 33])
print(dq)  # deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
dq.extendleft([10, 20, 30, 40])
print(dq)  # deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

列表和双向队列的方法(不包括由对象实现的方法):

方法或属性列表双向队列详解
s.__add__(s2)s + s2,拼接
s.__iadd__(s2)s += s2,就地拼接
s.append(e)在尾部添加一个新元素
s.appendleft(e)在尾部添加一个新元素
s.clear()删除所有元素
s.__contains__(e)s 是否包含 e
s.copy()列表的浅复制
s.__copy__()对 copy.copy(浅复制)的支持
s.count(e)e 在 s 中出现的次数
s.__delitem__(p)把位于 p 的元素删除
s.extend(i)将可迭代对象 i 中的元素添加到尾部
s.extendleft(i)将可迭代对象 i 中的元素添加到头部
s.__getitem__(p)s[p],获取位置 p 的元素
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()6删除最后或者是(可选的)位于 p 的元素,并返回它的值
s.popleft()移除第一个元素并返回它的值
s.remove(e)删除 s 中的第一次出现的 e
s.reverse()就地把 s 的元素倒序排列
s.__reversed__()返回 s 的倒序迭代器
s.rotate(n)把 n 个元素从队列的一端移到另一端
s.__setitem__(p,e)s[p] = e,把元素 e 放在位置p,替代已经在那个位置的元素
s.sort([key],[reverse])就地对 s 中的元素进行排序,可选的参数有键(key)和是否倒序(reverse)

除了 deque 之外,还有些其他的 Python 标准库也有对队列的实现。

queue7
提供了同步(线程安全)类 QueueLifoQueuePriorityQueue,不同的线程可以利用这些数据类型来交换信息。这三个类的构造方法都有一个可选参数 maxsize,它接收正整数作为输入值,用来限定队列的大小。但是在满员的时候,这些类不会扔掉旧的元素来腾出位置。相反,如果队列满了,它就会被锁住,直到另外的线程移除了某个元素而腾出了位置。这一特性让这些类很适合用来控制活跃线程的数量。

multiprocessing8
这个包实现了自己的 Queue,它跟 queue.Queue 类似,是设计给进程间通信用的。同时还有一个专门的multiprocessing.JoinableQueue 类型,可以让任务管理变得更方便。

asyncio:
Python 3.4 新提供的包,里面有QueueLifoQueuePriorityQueueJoinableQueue,这些类受到 queuemultiprocessing 模块的影响,但是为异步编程里的任务管理提供了专门的便利。

heapq9
跟上面三个模块不同的是,heapq 没有队列类,而是提供了heappushheappop 方法,让用户可以把可变序列当作堆队列或者优先队列来使用。

杂谈

Python 入门教材往往会强调列表是可以同时容纳不同类型的元素的,但是实际上这样做并没有什么特别的好处。元组则恰恰相反,它经常用来存放不同类型的的元素。这也符合它的本质,元组就是用作存放彼此之间没有关系的数据的记录。

python进阶书目串烧(一)—— 特殊方法、序列数组、列表推导、生成器表达

python进阶书目串烧(二)—— 元组拆包、具名元组、元组对比列表、切片

python进阶书目串烧(三)—— 序列、排序、列表对比数组

python进阶书目串烧(四)—— 内存视图、NumPy、列表对比双向队列

python进阶书目串烧(五)—— 泛映射类型、字典推导、映射的弹性键查询

python进阶书目串烧(六)—— 字典变种、不可变映射类型、集合推导

python进阶书目串烧(七)—— 字典原理、字典与集合特征对比


  1. B2中涉及到memoryview使用的位置:5.9 读取二进制数据到可变缓冲区中、5.10 内存映射的二进制文件、6.12 读取嵌套和可变长二进制数据、11.13 发送与接收大型数组 ↩︎

  2. 内存视图其实是泛化和去数学化的 NumPy 数组。它让你在不需要复制内容的前提下,在数据结构之间共享内存。其中数据结构可以是任何形式,比如 PIL 图片、SQLite 数据库和 NumPy 的数组,等等。这个功能在处理大型数据集合的时候非常重要。 ↩︎

  3. B2中涉及到numpy使用的位置:3.6 复数的数学运算、3.9 大型数组运算、3.10 矩阵与线性代数运算、6.11 读写二进制数组数据、11.13 发送与接收大型数组、15.3 编写扩展函数操作数组、15.11 用 Cython 写高性能的数组操作 ↩︎

  4. SciPy 是基于 NumPy 的另一个库,它提供了很多跟科学计算有关的算法,专为线性代数、数值积分和统计学而设计。SciPy 的高效和可靠性归功于其背后的 C 和 Fortran 代码,而这些跟计算有关的部分都源自于Netlib 库。换句话说,SciPy 把基于 C 和 Fortran的工业级数学计算功能用交互式且高度抽象的 Python 包装起来,让科学家如鱼得水。 ↩︎

  5. B2中涉及到collections.deque使用的位置:1.3 保留最后 N 个元素、4.6 带有外部状态的生成器函数、12.12 使用生成器代替线程 ↩︎

  6. a_list.pop(p)这个操作只能用于列表,双向队列的这个方法不接收参数。 ↩︎

  7. B2中涉及到queue使用的位置:7.11 内联回调函数、12.3 线程间通信、12.7 创建一个线程池、12.10 定义一个 Actor 任务、12.13 多个线程队列轮询;B3中涉及到queue使用的位置:第39条:用Queue来协调各线程之间的工作 ↩︎

  8. B2中涉及到multiprocessing使用的位置:7.8 减少可调用对象的参数个数、7.11 内联回调函数、11.7 在不同的 Python 解释器之间交互、11.8 实现远程方法调用、11.11 进程间传递 Socket 文件描述符、12.1 启动与停止线程、12.9 Python 的全局锁问题 ↩︎

  9. B2中涉及到heapq使用的位置:1.4 查找最大或最小的 N 个元素、1.5 实现一个优先级队列、4.15 顺序迭代合并后的排序迭代对象、12.2 判断线程是否已经启动 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值