zoukankan      html  css  js  c++  java
  • 数组、内存视图、双向队列

    数组

    如果我们需要一个只包含数字的列表,那么 array.array 比 list 更高效。数组支持所有跟可变序列有关的操作,包括 .pop、.insert 和.extend。另外,数组还提供从文件读取和存入文件的更快的方法,如.frombytes 和 .tofile。

    Python 数组跟 C 语言数组一样精简。创建数组需要一个类型码,这个类型码用来表示在底层的 C 语言应该存放怎样的数据类型。比如 b 类型码代表的是有符号的字符(signed char),因此 array('b') 创建出的数组就只能存放一个字节大小的整数,范围从 -128 到 127,这样在序列很大的时候,我们能节省很多空间。而且 Python 不会允许你在数组里存放除指定类型之外的数据。

    示例 2-20 展示了从创建一个有 1000 万个随机浮点数的数组开始,到如何把这个数组存放到文件里,再到如何从文件读取这个数组。

    示例 2-20 一个浮点型数组的创建、存入文件和从文件读取的过程

    >>> from array import array ➊
    >>> from random import random
    >>> floats = array('d', (random() for i in range(10**7))) ➋
    >>> floats[-1] ➌
    0.07802343889111107
    >>> 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.07802343889111107
    >>> floats2 == floats ➑
    True

    ❶ 引入 array 类型。
    ❷ 利用一个可迭代对象来建立一个双精度浮点数组(类型码是 'd'),
    这里我们用的可迭代对象是一个生成器表达式。
    ❸ 查看数组的最后一个元素。
    ❹ 把数组存入一个二进制文件里。
    ❺ 新建一个双精度浮点空数组。
    ❻ 把 1000 万个浮点数从二进制文件里读取出来。
    ❼ 查看新数组的最后一个元素。
    ❽ 检查两个数组的内容是不是完全一样

    从上面的代码我们能得出结论,array.tofile 和 array.fromfile 用起来很简单。把这段代码跑一跑,你还会发现它的速度也很快。一个小试验告诉我,用 array.fromfile 从一个二进制文件里读出 1000 万个双精度浮点数只需要 0.1 秒,这比从文本文件里读取的速度要快 60倍,因为后者会使用内置的 float 方法把每一行文字转换成浮点数。

    另外,使用 array.tofile 写入到二进制文件,比以每行一个浮点数的方式把所有数字写入到文本文件要快 7 倍。另外,1000 万个这样的数在二进制文件里只占用 80 000 000 个字节(每个浮点数占用 8 个字节,不需要任何额外空间),如果是文本文件的话,我们需要 181 515 739个字节。

    另外一个快速序列化数字类型的方法是使用pickle模块。pickle.dump 处理浮点数组的速度几乎跟 array.tofile 一样快。不过前者可以处理几乎所有的内置数字类型,包含复数、嵌套集合,甚至用户自定义的类。前提是这些类没有什么特别复杂的实现。

    内存视图

    memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片。memoryview 的概念受到了 NumPy 的启发。

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

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

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

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

      示例 2-23 使用双向队列

    >>> 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.rotate(-4)
    >>> dq
    deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
    >>> dq.appendleft(-1) ➌
    >>> dq
    deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
    >>> dq.extend([11, 22, 33]) ➍
    >>> dq
    deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
    >>> dq.extendleft([10, 20, 30, 40]) ➎
    >>> dq
    deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

    ❶ maxlen 是一个可选参数,代表这个队列可以容纳的元素的数量,而
    且一旦设定,这个属性就不能修改了。
    ❷ 队列的旋转操作接受一个参数 n,当 n > 0 时,队列的最右边的 n
    个元素会被移动到队列的左边。当 n < 0 时,最左边的 n 个元素会被
    移动到右边。
    ❸ 当试图对一个已满(len(d) == d.maxlen)的队列做尾部添加操作
    的时候,它头部的元素会被删除掉。注意在下一行里,元素 0 被删除
    了。
    ❹ 在尾部添加 3 个元素的操作会挤掉 -1、1 和 2。
    ❺ extendleft(iter) 方法会把迭代器里的元素逐个添加到双向队列
    的左边,因此迭代器里的元素会逆序出现在队列里。

    双向队列实现了大部分列表所拥有的方法,也有一些额外的符合自身设计的方法,比如说 popleft 和 rotate。但是为了实现这些方法,双向队列也付出了一些代价,从队列中间删除元素的操作会慢一些,因为它只对在头尾的操作进行了优化。

    append 和 popleft 都是原子操作,也就说是 deque 可以在多线程程序中安全地当作先进先出的栈使用,而使用者不需要担心资源锁的问题。

     

     

    人生就是要不断折腾
  • 相关阅读:
    对类对象使用new时地址分配的情况
    c++堆与栈的简单认识
    多态公有继承
    Cookie & Session & JSP入门
    Response & ServletContext
    Java网络编程篇文章阅读顺序
    URL编程
    UDP网络编程
    TCP网络编程
    InetAddress类的使用
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/11581056.html
Copyright © 2011-2022 走看看