在 Python 中迭代时从列表中删除项目而不使用额外的内存
我的问题很简单:我有一个很长的元素列表,我想遍历这些元素并根据条件检查每个元素。根据条件的结果,我想删除列表的当前元素,并像往常一样继续迭代它。
我已经阅读了有关此问题的其他一些主题。提出了两种解决方案。要么从列表中创建一个字典(这意味着在我的例子中复制已经填满所有 RAM 的所有数据)。要么反向遍历列表(这打破了我想要实现的算法的概念)。
还有比这更好或更优雅的方法吗?
def walk_list(list_of_g):
g_index = 0
while g_index < len(list_of_g):
g_current = list_of_g[g_index]
if subtle_condition(g_current):
list_of_g.pop(g_index)
else:
g_index = g_index + 1
My problem is simple: I have a long list of elements that I want to iterate through and check every element against a condition. Depending on the outcome of the condition I would like to delete the current element of the list, and continue iterating over it as usual.
I have read a few other threads on this matter. Two solutions seam to be proposed. Either make a dictionary out of the list (which implies making a copy of all the data that is already filling all the RAM in my case). Either walk the list in reverse (which breaks the concept of the alogrithm I want to implement).
Is there any better or more elegant way than this to do it ?
def walk_list(list_of_g):
g_index = 0
while g_index < len(list_of_g):
g_current = list_of_g[g_index]
if subtle_condition(g_current):
list_of_g.pop(g_index)
else:
g_index = g_index + 1
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
还有
感谢 Dave Kirby
and also
Thanks to Dave Kirby
如果您绝对必须从原始列表中删除项目,并且您没有足够的内存来制作副本,那么这里有一个替代答案 - 自己将项目向下移动到列表中:
这将移动每个项目(实际上是一个指向每个项目的指针) item) 恰好出现一次,因此时间复杂度为 O(N)。函数末尾的 del 语句将删除列表末尾的任何不需要的项目,并且我认为 Python 足够智能,可以调整列表的大小,而无需为列表的新副本分配内存。
Here is an alternative answer for if you absolutely have to remove the items from the original list, and you do not have enough memory to make a copy - move the items down the list yourself:
This will move each item (actually a pointer to each item) exactly once, so will be O(N). The del statement at the end of the function will remove any unwanted items at the end of the list, and I think Python is intelligent enough to resize the list without allocating memory for a new copy of the list.
从列表中删除项目的成本很高,因为 python 必须将 g_index 之上的所有项目复制到一个位置。如果要删除的项目数量与列表 N 的长度成正比,那么您的算法将是 O(N**2)。如果列表足够长,足以填满您的 RAM,那么您将等待很长时间才能完成。
创建列表的过滤副本会更有效,可以使用 Marcelo 所示的列表理解,或者使用 filter 或 itertools.ifilter 函数:
如果您不需要使用新列表并且只想迭代一次,那么最好使用 ifilter,因为这不会创建第二个列表:
removing items from a list is expensive, since python has to copy all the items above g_index down one place. If the number of items you want to remove is proportional to the length of the list N, then your algorithm is going to be O(N**2). If the list is long enough to fill your RAM then you will be waiting a very long time for it to complete.
It is more efficient to create a filtered copy of the list, either using a list comprehension as Marcelo showed, or use the filter or itertools.ifilter functions:
If you do not need to use the new list and only want to iterate over it once, then it is better to use ifilter since that will not create a second list:
内置过滤器功能就是为了做到这一点:
The built-in filter function is made just to do this:
这个怎么样?
它返回新列表,但微妙_条件除外
How about this?
its return the new list with exception from subtle_condition
为了简单起见,使用列表理解:
当然,这不会改变原始列表,因此调用代码必须不同。
如果你真的想改变列表(很少是最好的选择),向后走更简单:
For simplicity, use a list comprehension:
Of course, this doesn't alter the original list, so the calling code would have to be different.
If you really want to mutate the list (rarely the best choice), walking backwards is simpler:
听起来像是过滤器功能的一个非常好的用例。
然而,这不会在迭代时删除列表(我也不推荐这样做)。如果出于内存空间(或其他性能原因)您确实需要它,您可以执行以下操作:
Sounds like a really good use case for the filter function.
This, however, will not delete the list while iterating (nor I recommend it). If for memory-space (or other performance reasons) you really need it, you can do the following:
如果执行反向迭代,则可以动态删除元素,而不会影响您将访问的下一个索引:
enumerate(reversed(numbers))
只是一种风格选择。如果您更容易理解,您可以使用范围:如果您需要按顺序遍历列表,则可以在反向迭代之前和之后使用
.reverse()
就地反转它。这也不会重复您的列表。If you perform a reverse iteration, you can remove elements on the fly without affecting the next indices you'll visit:
The
enumerate(reversed(numbers))
is just a stylistic choice. You may use a range if that's more legible to you:If you need to travel the list in order, you can reverse it in place with
.reverse()
before and after the reversed iteration. This won't duplicate your list either.