在内存中保持大列表的替代方案(Python)

14 投票
9 回答
25569 浏览
提问于 2025-04-15 17:32

如果我在Python中有一个列表(或者数组、字典等等),这个列表可能会超过可用的内存地址空间(32位的Python),那么我有哪些选择,以及这些选择的速度如何?(除了不创建这么大的列表)

这个列表可能会超过内存,但我事先无法知道。一旦它开始超过75%的内存,我希望不再将这个列表保留在内存中(或者至少不再保留新添加的项),有没有办法在这个过程中转换为基于文件的方式?

有哪些最佳的文件存储选项(进出速度)?

我只需要存储一个简单的数字列表,不需要随机访问第N个元素,只需要添加和删除操作。

9 个回答

6

这个问题的答案其实是“要看情况”。

你在列表里存的是什么?是字符串?整数?还是对象?

列表被写入的频率和读取的频率相比如何?是只在末尾添加内容,还是可以修改或在中间插入?

如果你只是往末尾添加内容,那么写入一个简单的文件可能是最简单的解决办法。

如果你存的是大小不一的对象,比如字符串,那可以在内存中保留每个字符串开始位置的索引,这样读取起来会更快。

如果你想要字典那样的功能,可以看看数据库模块,比如 dbm、gdbm、bsddb 等等。

如果你需要随机写入,那么可能使用 SQL 数据库会更好。

无论你选择什么,写入硬盘的速度都会比在内存中慢很多,但如果不知道数据将如何使用,就很难给出更具体的建议。

补充:根据你更新的需求,我建议使用一个简单的文件,并在内存中保留最近的 N 个元素的缓冲区。

8

有很多种方法可以把你的列表数据存储在文件里,而不是放在内存中。你选择哪种方法,完全取决于你需要对数据进行什么样的操作。比如,你需要随机访问第N个元素吗?你需要遍历所有元素吗?你会不会需要查找符合某些条件的元素?列表里的元素是什么样的?你只是在列表的末尾插入数据,还是也会在中间插入?你能否把一些元数据保存在内存中,而把大部分数据放在磁盘上?等等等等。

一种可能的方法是把数据以关系型的方式结构化,然后存储在SQLite数据库中。

14

如果你的“数字”比较简单(比如最多4个字节的有符号或无符号整数,或者4个或8个字节的浮点数),我推荐使用标准库中的array模块,这样可以在内存中存储几百万个数字(就像你的“虚拟数组”的“尖端”),同时用一个二进制文件(以二进制读写方式打开)来支持磁盘上的其他结构。array.array有非常快速的fromfiletofile方法,可以方便地在数据之间移动。

也就是说,假设我们用的是无符号长整型数字,代码大概是这样的:

import os

# no more than 100 million items in memory at a time
MAXINMEM = int(1e8)

class bigarray(object):
  def __init__(self):
    self.f = open('afile.dat', 'w+')
    self.a = array.array('L')
  def append(self, n):
    self.a.append(n)
    if len(self.a) > MAXINMEM:
      self.a.tofile(self.f)
      del self.a[:]
  def pop(self):
    if not len(self.a):
      try: self.f.seek(-self.a.itemsize * MAXINMEM, os.SEEK_END)
      except IOError: return self.a.pop()  # ensure normal IndexError &c
      try: self.a.fromfile(self.f, MAXINMEM)
      except EOFError: pass
      self.f.seek(-self.a.itemsize * MAXINMEM, os.SEEK_END)
      self.f.truncate()
    return self.a.pop()

当然,你可以根据需要添加其他方法(比如跟踪总长度,添加extend等),但如果你只需要popappend这两个方法,这个方案就足够用了。

撰写回答