likes
comments
collection
share

流畅的python--小技巧总结2

作者站长头像
站长
· 阅读数 14

对于百万级以上数据的读写效率优化

在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也是一个很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素

以下介绍两个使用双向队列的小案例:

  1. 实现一个滑动窗口

双向队列可以用于实现一个滑动窗口,该窗口可以在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是序列的长度。

  1. 实现一个循环队列

双向队列可以用于实现一个循环队列,该队列可以在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)