当列表不是首选时:如果要存放1000万个浮点数,数组(array)的效率要高的多,因为存储的不是float对象而是数字的机器翻译(字节表述);如果要频繁对序列进行先进先出操作,deque(双向队列)速度更快

1. 数组(array)和内存视图(memoryview)

>>> from array import array
>>> from random import random

>>> floats = array('d', (random() for i in range(10**7))) # d表示double
>>> floats[-1]
0.5403817388275506

>>> 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.5403817388275506

>>> floats[-1] == floats2[-1]
True

内存视图可以在内存层面对数组中某个字节进行修改,平时不常见,暂不做记录

2. 双向队列(deque)

collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型

>>> from collections import deque
>>> dq = deque(range(10), maxlen=10) # maxlen代表这个队列可以容纳的元素的数量,且无法更改
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3) # n > 0 时,队列的最右边的 n 个元素会被移动到队列的左边;反之左边移到右边
>>> 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) # 0 被删掉了
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11, 22, 33]) # 在尾部添加 3 个元素的操作会挤掉 -1、 1 和 2
>>> 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)

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

除了deque之外,还有其他的Python标准库也有对队列的实现,包括:queuemultiprocessingasyncioheapqheapq没有提供队列类,而是提供了heappushheappop 方法,让用户可以把可变序列当作堆队列或者优先队列来使用

3. heapq

heapq有两种方式创建堆:

  1. 使用一个空列表,然后使用heapq.heappush()函数把值加入堆中
  2. 使用heap.heapify(list)转换列表成为堆结构
import heapq

# 第一种
nums = [2, 3, 5, 1, 54, 23, 132]
heap = []
for num in nums:
    heapq.heappush(heap, num)  # 加入堆

print(heap[0])  # 如果只是想获取最小值而不是弹出,使用heap[0]
print([heapq.heappop(heap) for _ in range(len(nums))])  # 堆排序结果
# out: [1, 2, 3, 5, 23, 54, 132]


# 第二种
nums = [2, 3, 5, 1, 54, 23, 132]
heapq.heapify(nums)
print([heapq.heappop(nums) for _ in range(len(nums))])  # 堆排序结果
# out: [1, 2, 3, 5, 23, 54, 132]
  • 如果需要删除堆中最小元素并加入一个元素,可以使用 heapq.heaprepalce() 函数,堆的大小不变,同heapq.heappushpop(heap,item)
import heapq

nums = [1, 2, 4, 5, 3]
heapq.heapify(nums)

heapq.heapreplace(nums, 23)

print([heapq.heappop(nums) for _ in range(len(nums))])
# out: [2, 3, 4, 5, 23]
  • 如果需要获取堆中最大或最小的范围值,则可以使用heapq.nlargest()heapq.nsmallest() 函数
import heapq

nums = [1, 3, 4, 5, 2]
print(heapq.nlargest(3, nums))
print(heapq.nsmallest(3, nums))

"""
输出:
[5, 4, 3]
[1, 2, 3]
"""

这两个函数还接受一个key参数,用于dict或其他数据结构类型

import heapq
from pprint import pprint
portfolio = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
pprint(cheap)
pprint(expensive)

"""
输出:
[{'name': 'YHOO', 'price': 16.35, 'shares': 45},
{'name': 'FB', 'price': 21.09, 'shares': 200},
{'name': 'HPQ', 'price': 31.75, 'shares': 35}]
[{'name': 'AAPL', 'price': 543.22, 'shares': 50},
{'name': 'ACME', 'price': 115.65, 'shares': 75},
{'name': 'IBM', 'price': 91.1, 'shares': 100}]
"""