Python:修改列表时的内存使用和优化 问题

Python:修改列表时的内存使用和优化 问题,python,optimization,memory,list,iteration,Python,Optimization,Memory,List,Iteration,我关心的是:我正在一个经典的python列表中存储一个大型数据集,为了处理数据,我必须在列表上迭代几次,对元素执行一些操作,并且经常从列表中弹出一个项目 从Python列表中删除一项似乎需要O(N),因为Python必须在一个地方复制元素上方的所有项。此外,由于要删除的项的数量与列表中元素的数量近似成比例,因此会产生O(N^2)算法 我希望找到一个经济高效的解决方案(时间和内存方面)。我研究了我在互联网上能找到的东西,并在下面总结了我的不同选择。哪一个是最好的候选人 保存本地索引: 这避免了增加

我关心的是:我正在一个经典的python列表中存储一个大型数据集,为了处理数据,我必须在列表上迭代几次,对元素执行一些操作,并且经常从列表中弹出一个项目

从Python列表中删除一项似乎需要O(N),因为Python必须在一个地方复制元素上方的所有项。此外,由于要删除的项的数量与列表中元素的数量近似成比例,因此会产生O(N^2)算法

我希望找到一个经济高效的解决方案(时间和内存方面)。我研究了我在互联网上能找到的东西,并在下面总结了我的不同选择。哪一个是最好的候选人

保存本地索引: 这避免了增加索引变量,但最终成本与原始版本相同。它还打破了dosomestuff(item)的逻辑,dosomestuff(item)希望按照原始列表中显示的顺序处理它们

制作新列表: 这是一种从列表中删除元素的非常简单的策略,并且需要大量内存,因为必须制作列表的几乎完整副本

使用列表理解: 这是非常优雅的,但在封面下,它会再次浏览整个列表,并且必须复制其中的大部分元素。我的直觉是,至少在内存方面,这个操作可能比原始的del语句花费更多。请记住,somelist可能非常庞大,任何每次运行只迭代一次的解决方案都可能永远获胜

使用过滤器功能: 这也会创建一个占用大量RAM的新列表

使用itertools的过滤功能: 此版本的筛选器调用不会创建新的列表,但不会对每个打破算法逻辑的项调用dosomestuff。我包含这个例子只是为了创建一个详尽的列表

行走时将项目向上移动 这是一个微妙的方法,似乎具有成本效益。我认为它会将每个项目(或指向每个项目的指针?)精确地移动一次,从而产生一个O(N)算法。最后,我希望Python能够足够智能地在最后调整列表的大小,而无需为列表的新副本分配内存。不过我不确定

放弃Python列表:
这种类型的对象在某种程度上类似于python列表。然而,元素的删除保证为O(1)。我不想在这里讨论,因为这几乎在任何地方都需要大量的代码重构。

如果不知道使用此列表的具体操作,很难确切知道在这种情况下什么是最好的。如果您的处理阶段依赖于列表元素的当前索引,那么这将不起作用,但如果不起作用,那么您似乎放弃了最具python风格(在许多方面也是最简单的)的方法:生成器

如果您所做的只是对每个元素进行迭代,以某种方式对其进行处理,那么无论是否将该元素包含在列表中,都可以使用生成器。这样,您就不需要将整个iterable存储在内存中

def process_and_generate_data(source_iterable):
    for item in source_iterable:
        dosomestuff(item)
        if not somecondition(item):
            yield item

您需要有一个处理循环来处理持久化已处理的iterable(将其写回文件或其他文件),或者,如果您有多个处理阶段,您希望将其分为不同的生成器,您可以让处理循环将一个生成器传递到下一个生成器。

您没有提供足够的信息,我无法很好地回答这个问题。我不太了解您的用例,无法告诉您,如果您必须针对时间进行优化,哪些数据结构将为您带来所需的时间复杂性。典型的解决方案是构建一个新的列表,而不是重复删除,但显然这会使内存使用量翻一番

如果存在内存使用问题,您可能希望放弃使用内存中的Python构造,而使用磁盘上的数据库。许多数据库都可用,并随Python提供。根据您的使用情况和内存需求的紧张程度,
array.array
或numpy可能会对您有所帮助,但这在很大程度上取决于您需要做什么<代码>数组。数组将具有与列表相同的时间复杂性,numpy数组有点类似,但工作方式不同。使用惰性迭代器(如生成器和
itertools
模块中的东西)通常可以将内存使用量减少n倍

使用数据库将缩短从任意位置删除项目的时间(尽管如果这很重要,订单将丢失)。使用
dict
也可以做到这一点,但可能会占用大量内存

你也可以考虑替换一个可能会得到你想要的妥协的列表。我不相信它会大幅增加内存使用,但它会将项目删除更改为O(logn)。当然,这是以使其他业务更加昂贵为代价的

我必须通过测试才能相信,双链表实现中内存使用的常数因子将小于通过创建一个新列表得到的2。我真的很怀疑

我认为,为了得到更具体的答案,你必须分享更多关于你的问题课的信息,但一般的建议是

  • 在一个列表上迭代,并在进行时生成一个新列表(或者在需要时使用生成器生成项目)。如果你真的需要一个列表,那么它的记忆因子是2,这可以很好地扩展,但是如果你的记忆周期很短,它就没有帮助了
  • 如果内存不足,而不是进行微优化,则可能需要一个磁盘上的数据库或将数据存储在文件中

从您的描述中,它听起来像是一副德克(“甲板”)正是您想要的:

通过反复调用pop()对其进行“迭代”,然后,如果希望将弹出的项保留在deque中,请重新
while processingdata:
    for i in xrange(len(somelist) - 1, -1, -1):
        dosomestuff(item)
        if somecondition(somelist, i):
            somelist.pop(i)
while processingdata:
    for i, item in enumerate(somelist):
        dosomestuff(item)
    newlist = []
    for item in somelist:
        if somecondition(item):
            newlist.append(item)
    somelist = newlist
    gc.collect()
while processingdata:
    for i, item in enumerate(somelist):
        dosomestuff(item)
    somelist[:] = [x for x in somelist if somecondition(x)]
while processingdata:
    for i, item in enumerate(somelist):
        dosomestuff(item)
    somelist = filter(lambda x: not subtle_condition(x), somelist)
from itertools import ifilterfalse
while processingdata:
     for item in itertools.ifilterfalse(somecondtion, somelist):
         dosomestuff(item)
while processingdata:
    index = 0
    for item in somelist:
        dosomestuff(item)
        if not somecondition(item):
            somelist[index] = item
            index += 1
    del somelist[index:]
class Doubly_Linked_List:
    def __init__(self):
        self.first = None
        self.last = None
        self.n = 0
    def __len__(self):
        return self.n
    def __iter__(self):
        return DLLIter(self)
    def iterator(self):
        return self.__iter__()
    def append(self, x):
        x = DLLElement(x)
        x.next = None
        if self.last is None:
            x.prev = None
            self.last = x
            self.first = x
            self.n = 1
        else:
            x.prev = self.last
            x.prev.next = x
            self.last = x
            self.n += 1

class DLLElement:
    def __init__(self, x):
    self.next = None
    self.data = x
    self.prev = None

class DLLIter:
    etc...
def process_and_generate_data(source_iterable):
    for item in source_iterable:
        dosomestuff(item)
        if not somecondition(item):
            yield item
from collections import deque
d = deque(range(30))
n = deque()

print d

while True:
    try:
        item = d.popleft()
    except IndexError:
        break

    if item % 3 != 0:
        n.append(item)

print n
while processingdata:
    retained = []
    for item in somelist:
        dosomething(item)
        if not somecondition(item):
            retained.append(item)
    somelist = retained
def process_and_decide(item):
    dosomething(item)
    return not somecondition(item)

while processingdata:
    somelist = [item for item in somelist if process_and_decide(item)]
def inplace_filter(func, list_):
    pos = 0
    for item in list_:
        if func(item):
            list_[pos] = item
            pos += 1
    del list_[pos:]

while processingdata:
    inplace_filter(process_and_decide, somelist)