如何在使用 range() 函数从列表中删除项目时迭代列表? [复制]

Posted

技术标签:

【中文标题】如何在使用 range() 函数从列表中删除项目时迭代列表? [复制]【英文标题】:How to iterate a list while deleting items from list using range() function? [duplicate] 【发布时间】:2012-05-19 08:22:31 【问题描述】:

这是我在尝试学习 python 编程时遇到的最常见的问题。问题是,当我尝试使用“range()”函数迭代列表以检查列表中的给定项目是否满足给定条件,如果是则删除它,它总是会给出“IndexError”。那么,有没有一种特殊的方法可以在不使用任何其他中间列表或“while”语句的情况下做到这一点?下面是一个例子:

l = range(20)
for i in range(0,len(l)):
  if l[i] == something:
    l.pop(i)

【问题讨论】:

查看过滤器内置功能。这就是你想要的。 你为什么要在条件是 i==something 的情况下编写这样的代码?在这种情况下,我不是一个非常有用的比较项目 @TJD 你是莱特,我的意思是“l[i]”。 【参考方案1】:

首先,您永远不想在 Python 中迭代类似的东西。迭代实际对象,而不是索引:

l = range(20)
for i in l:
    ...

您出错的原因是您正在删除一个项目,因此后来的索引不复存在。

现在,您不能在循环访问列表时修改它,但这不是问题。更好的解决方案是在此处使用list comprehension,以过滤掉多余的项目。

l = range(20)
new_l = [i for i in l if not i == something]

您也可以使用the filter() builtin,尽管在大多数情况下这往往不清楚(在您需要lambda 的地方会更慢)。

另请注意,在 Python 3.x 中,range() 生成的是生成器,而不是列表。

使用更具描述性的变量名称也是一个好主意 - 我假设这里是示例,但是像 il 这样的名称很难阅读并且更容易引入错误。

编辑:

如果您希望更新现有列表,如 cmets 中所指出的,您可以使用切片语法依次替换列表中的每个项目 (l[:] = new_l)。也就是说,我认为这种情况是非常糟糕的设计。您不希望一段代码依赖以这种方式从另一段代码更新的数据。

编辑 2:

如果出于任何原因,您在遍历项目时需要索引,这就是 the enumerate() builtin 的用途。

【讨论】:

假设您只有一个名称引用该列表。 @DavidHeffernan 如果需要,您可以将新列表重新分配给旧列表。 如果您在代码中有其他无法立即控制的引用,则不会。我知道有点牵强,但有时需要就地修改。 @DavidHeffernan,所以在这种情况下使用切片分配来就地更新列表。 @DavidHeffernan 然后可以使用切片语法依次替换列表中的每一项(l[:] = new_l)。也就是说,我认为这种情况是非常糟糕的设计。您不希望一段代码依赖以这种方式从另一段代码更新的数据。【参考方案2】:

你总是可以用列表理解来做这种事情:

newlist=[i for i in oldlist if not condition ]

【讨论】:

列表理解...很好。来自我的 +1【参考方案3】:

正如其他人所说,遍历列表并创建一个仅包含您想要保留的项目的新列表。

使用切片分配就地更新原始列表。

l[:] = [item for item in l if item != something]

【讨论】:

【参考方案4】:

你应该从另一面来看问题:当一个元素等于“某物”时,将它添加到一个列表中。使用列表理解:

l = [i for i in xrange(20) if i != something]

【讨论】:

【参考方案5】: 你不应该使用for i in range(0,len(l)):,如果你需要索引,使用for i, item in enumerate(l):,如果不需要,使用for item in l: 您不应该操纵您正在迭代的结构。遇到这种情况时,请改为遍历副本 不要命名变量 l(可能被误认为 1 或 I) 如果要过滤列表,请明确执行。使用 filter() 或列表推导

顺便说一句,在你的情况下,你也可以这样做:

while something in list_: list_.remove(something)

不过,这不是很有效。但根据上下文,它可能更具可读性。

【讨论】:

【参考方案6】:

您获得IndexError 的原因是您在for 循环中迭代时更改了列表的长度。基本上,这就是逻辑......

#-- Build the original list: [0, 1, 2, ..., 19]
l = range(20)

#-- Here, the range function builds ANOTHER list, in this case also [0, 1, 2, ..., 19]
#-- the variable "i" will be bound to each element of this list, so i = 0 (loop), then i = 1 (loop), i = 2, etc.
for i in range(0,len(l)):
    if i == something:
        #-- So, when i is equivalent to something, you "pop" the list, l.
        #-- the length of l is now *19* elements, NOT 20 (you just removed one)
        l.pop(i)
    #-- So...when the list has been shortened to 19 elements...
    #-- we're still iterating, i = 17 (loop), i = 18 (loop), i = 19 *CRASH*
    #-- There is no 19th element of l, as l (after you popped out an element) only
    #-- has indices 0, ..., 18, now.

还请注意,您正在根据列表的索引做出“弹出”决定,而不是列表的索引单元格中的内容。这很不寻常——这是你的意图吗?或者你有没有 意思更像...

if l[i] == something:
    l.pop(i)

现在,在您的具体示例中,(l[i] == i) 但这不是典型的模式。

尝试过滤器功能,而不是遍历列表。它是内置的(与许多其他列表处理功能一样:例如 map、sort、reverse、zip 等)

试试这个...

#-- Create a function for testing the elements of the list.
def f(x):
    if (x == SOMETHING):
        return False
    else:
        return True

#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where f(l[i]) is True, the element l[i] is kept and will be in the new list, m.
#-- Where f(l[i]) is False, the element l[i] is passed over and will NOT appear in m.
m = filter(f, l)

列表处理函数与“lambda”函数密切相关——在 Python 中,后者是简短的匿名函数。所以,我们可以把上面的代码改写成……

#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where lambda is True, the element l[i] is kept and will be in the new list, m.
#-- Where lambda is False, the element l[i] is passed over and will NOT appear in m.
m = filter(lambda x: (x != SOMETHING), l)

试一试,看看它是如何工作的!

【讨论】:

使用filter()lambda 比这里的列表理解更慢且不够清晰。 @Lattyware 同意。我对上面的列表理解解决方案+1。在这种情况下,我唯一需要注意的是,对于很多初学者来说,列表理解实际上不如更冗长的方法那么清晰。否则,我全力以赴。 与所有语言结构一样,在使用之前需要对其进行解释,是的,但我认为这并不意味着应该避免使用它们。 是的,我的意思是 l[i] 而不是“i”,编辑它。

以上是关于如何在使用 range() 函数从列表中删除项目时迭代列表? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试从列表中删除元素时,如何忽略 ValueError?

如何在颤动中从列表中删除项目时重新加载列表

从数组javascript中删除项目时重新呈现列表的问题

如何从离子列表中删除离子项目?

如何从列表中删除节点

python中如何表示1到1000?