流畅的python--菜鸟小技巧2
对于百万级以上数据的读写效率优化
在Python中,我们可以通过array模块来方便地创建和操作数组,array模块提供了性能优异的数组操作方法,可以使我们的代码更加高效。同时,Python也提供了读写二进制文件和普通文本文件的方法,对于一些需要处理大量数据
的程序,我们可以通过比较二者的性能来选择更加高效的操作方式。
读写二进制文件和普通文本文件的性能对比:
from array import array
import os
from time import time
# 生成测试数据
a = array('i', range(1000000))
# 测试 array.fromfile() 和 array.tofile()
t1 = time()
with open('test.bin', 'wb') as f:
a.tofile(f)
with open('test.bin', 'rb') as f:
b = array('i')
b.fromfile(f, len(a))
t2 = time()
print(f"array.fromfile() and array.tofile(): {t2-t1:.6f} seconds")
os.remove('test.bin')
# 测试普通文本文件读写
t3 = time()
with open('test.txt', 'w') as f:
for i in a:
f.write(str(i) + '\n')
with open('test.txt', 'r') as f:
b = []
for line in f:
try:
b.append(int(line.strip()))
except ValueError:
pass
t4 = time()
print(f"Text file read and write: {t4-t3:.6f} seconds")
运行上述代码会得到以下结果
array.fromfile() and array.tofile(): 0.021007 seconds Text file read and write: 7.798990 seconds
我们可以发现,在处理大量数据时,二进制文件读写的性能要远远优于普通文本文件读写。这是因为,在使用普通文本文件进行读写时,需要将每个元素转换成字符串并添加换行符,这些额外的操作会影响程序的性能
快速在有序序列中插入一个元素,并保持有序性
import bisect
my_list = [1, 3, 4, 7, 8, 10]
bisect.insort(my_list, 6)
print(my_list)
bisect.insort
使用二分查找算法来查找元素应该插入的位置,然后使用list.insert()
方法将元素插入到该位置。这个函数的时间复杂度为O(log n)
,其中n是序列的长度。
bisect.insort()函数的实现方式与bisect.bisect()函数类似。bisect.bisect()
函数返回元素应该插入的位置,而bisect.insort()函数则将元素插入到该位置
使用双向队列
collections.deque
类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型。而且如果想要有一种数据类型来存放“最近用到的几个元素”,deque也是一个很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素
以下介绍两个使用双向队列的小案例:
- 实现一个滑动窗口
双向队列可以用于实现一个滑动窗口,该窗口可以在O(k)的时间复杂度内在一个序列中滑动,其中k是窗口的大小。以下是一个简单的示例:
from collections import deque
def sliding_window(nums, k):
# 在序列nums中实现一个大小为k的滑动窗口
result = []
window = deque()
for i, num in enumerate(nums):
if i >= k and window[0] <= i - k:
window.popleft()
while window and nums[window[-1]] >= num:
window.pop()
window.append(i)
if i >= k - 1:
result.append(nums[window[0]])
return result
在这个示例中,sliding_window()函数使用双向队列实现了一个大小为k的滑动窗口。该函数首先创建一个空的双向队列window,然后遍历序列nums中的每个元素。在每个元素上,该函数执行以下操作:
- 如果队列中的第一个元素已经超出了窗口的范围,则将其从队列中删除。
- 如果队列中的最后一个元素大于等于当前元素,则将其从队列中删除,直到队列为空或者最后一个元素小于当前元素。
- 将当前元素的下标添加到队列的末尾。
- 如果当前元素的下标大于等于k-1,则将队列中的第一个元素添加到结果列表中。
这个函数的时间复杂度为O(n)
,其中n是序列的长度。
- 实现一个循环队列
双向队列可以用于实现一个循环队列,该队列可以在O(1)的时间复杂度内在队列两端进行插入和删除操作。以下是一个简单的示例:
from collections import deque
class CircularQueue:
# 初始化一个大小为k的循环队列
def __init__(self, k):
self.k = k
self.queue = deque(maxlen=k)
# 将一个元素插入到队列的尾部。如果队列已满,则返回False
def enQueue(self, value):
if len(self.queue) < self.k:
self.queue.append(value)
return True
else:
return False
# 将队列的头部元素删除。如果队列为空,则返回False
def deQueue(self):
if self.queue:
self.queue.popleft()
return True
else:
return False
# 返回队列的头部元素。如果队列为空,则返回-1
def Front(self):
return self.queue[0] if self.queue else -1
# 返回队列的尾部元素。如果队列为空,则返回-1
def Rear(self):
return self.queue[-1] if self.queue else -1
这个类的时间复杂度为O(1)
。
转载自:https://juejin.cn/post/7244352006814269500