【发布时间】:2011-02-07 12:46:18
【问题描述】:
问题
我关心的是:我将一个相对论大数据集存储在一个经典的 python 列表中,为了处理数据,我必须多次迭代列表,对元素执行一些操作,并且经常弹出一个项目的列表。
似乎从 Python 列表中删除一项需要花费 O(N),因为 Python 必须将现有元素上方的所有项复制到一个位置。此外,由于要删除的项目数与列表中的元素数大致成正比,这导致了 O(N^2) 算法。
我希望找到一种经济高效的解决方案(时间和内存方面)。我研究了我在互联网上可以找到的内容,并在下面总结了我的不同选择。哪一个是最佳人选?
保持本地索引:
while processingdata:
index = 0
while index < len(somelist):
item = somelist[index]
dosomestuff(item)
if somecondition(item):
del somelist[index]
else:
index += 1
这是我想出的原始解决方案。这不仅不是很优雅,而且我希望有更好的方法来保持时间和内存效率。
向后遍历列表:
while processingdata:
for i in xrange(len(somelist) - 1, -1, -1):
dosomestuff(item)
if somecondition(somelist, i):
somelist.pop(i)
这避免了增加索引变量,但最终成本与原始版本相同。它还破坏了 dosomestuff(item) 希望按照它们在原始列表中出现的顺序处理它们的逻辑。
制作新列表:
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)]
这非常优雅,但实际上它会再次遍历整个列表,并且必须复制其中的大部分元素。我的直觉是,至少在内存方面,这个操作可能比原来的 del 语句花费更多。请记住,somelist 可能很大,并且每次运行仅迭代一次的任何解决方案都可能总是获胜。
使用过滤功能:
while processingdata:
for i, item in enumerate(somelist):
dosomestuff(item)
somelist = filter(lambda x: not subtle_condition(x), somelist)
这也会创建一个占用大量 RAM 的新列表。
使用 itertools 的过滤功能:
from itertools import ifilterfalse
while processingdata:
for item in itertools.ifilterfalse(somecondtion, somelist):
dosomestuff(item)
此版本的过滤器调用不会创建新列表,但不会对破坏算法逻辑的每个项目调用 dosomestuff。我包含这个示例只是为了创建一个详尽的列表。
边走边上移项目
while processingdata:
index = 0
for item in somelist:
dosomestuff(item)
if not somecondition(item):
somelist[index] = item
index += 1
del somelist[index:]
这是一种看似划算的巧妙方法。我认为它只会移动每个项目(或指向每个项目的指针?)一次,从而导致 O(N) 算法。最后,我希望 Python 足够智能,可以在最后调整列表大小,而无需为列表的新副本分配内存。不过不确定。
放弃 Python 列表:
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...
这种类型的对象在一定程度上类似于 python 列表。但是,删除元素的保证是 O(1)。我不想去这里,因为这几乎需要在任何地方进行大量代码重构。
【问题讨论】:
-
性能比较在哪里?
-
另外,请注意,当您觉得需要写
xrange(len(somelist) - 1, -1, -1)时,也可以将其写为reversed(xrange(len(somelist)))。
标签: python optimization memory list iteration