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

Python 如何在使用range()函数从列表中删除项目时迭代列表?,python,Python,这是我在学习python编程时遇到的最常见的问题。问题是,当我尝试使用“range()”函数迭代列表以检查列表中的给定项是否满足给定条件,如果是,则删除它时,它将始终给出“IndexError”。那么,有没有一种特殊的方法可以在不使用任何其他中间列表或“while”语句的情况下实现这一点?以下是一个例子: l = range(20) for i in range(0,len(l)): if l[i] == something: l.pop(i) 首先,您永远不希望在Python中迭

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

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

首先,您永远不希望在Python中迭代类似的内容。迭代实际对象,而不是索引:

l = range(20)
for i in l:
    ...
错误的原因是您正在删除一个项,因此后面的索引不再存在

现在,在循环列表时不能修改列表,但这不是问题。更好的解决方案是在这里使用一个过滤器,过滤掉多余的项目

l = range(20)
new_l = [i for i in l if not i == something]
您也可以使用,尽管在大多数情况下这一点往往不清楚(在需要
lambda
的情况下更慢)

还要注意,在Python3.x中,
range()
生成一个生成器,而不是列表

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

编辑:

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

编辑2:


如果出于任何原因,在循环项目时需要索引,这就是目的。

您始终可以通过列表理解来完成这类工作:

newlist=[i for i in oldlist if not condition ]
l = [i for i in xrange(20) if i != something]

你应该从另一个角度看问题:当元素与“某物”相等时,将其添加到列表中。具有列表理解能力:

newlist=[i for i in oldlist if not condition ]
l = [i for i in xrange(20) if i != something]
  • 您不应该对范围(0,len(l))中的i使用
    ,如果需要索引,请对枚举(l)中的i,项使用
    ,如果不需要,则对l中的项使用
  • 您不应该操作正在迭代的结构。如果要这样做,请在副本上迭代
  • 不要命名变量l(可能被误认为是1或I)
  • 如果要筛选列表,请显式执行此操作。使用
    filter()
    或列表理解
顺便说一句,在你的情况下,你也可以做:

while something in list_: list_.remove(something)

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

正如其他人所说,迭代列表并创建一个新列表,其中只包含您想要保留的项目

使用切片指定来更新原始列表

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

您得到一个
索引器的原因是,您在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)
,但这不是典型的模式

请尝试filter函数,而不是遍历列表。它是一个内置的(像许多其他列表处理功能一样:例如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中,lambda函数是简短的匿名函数。因此,我们可以将上面的代码重新编写为

#-- 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)

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

检查过滤器的内置功能。这就是你想要的。为什么你要写这样的代码,条件是i==something?在这种情况下,我不是一个非常有用的项目comparison@TJD您是wright,我的意思是“l[I]”。这假设您只有一个名称引用该列表。@DavidHeffernan如果愿意,您可以将新列表重新分配给旧列表。如果代码中有其他超出您直接控制的引用,则不能。我知道有点牵强,但有时需要就地修改。@DavidHeffernan,因此在这种情况下,请使用切片分配就地更新列表。@DavidHeffernan然后您可以使用切片语法依次替换列表中的每个项目(
l[:]=new\u l
)。也就是说,我认为这种情况是非常糟糕的设计。您不希望一段代码依赖于以这种方式从另一段代码更新的数据。使用
filter()
lambda
比这里的列表理解更慢、更不清晰。@Lattyware同意。我+1的列表理解解决方案,上面。在这种情况下,我唯一要注意的是,对于许多初学者来说,列表理解实际上不如更详细的方法清晰。否则,我完全赞成。就像所有的语言结构一样,它们需要在使用之前得到解释,是的,但我不认为这意味着它们应该被避免。是的,我的意思是l[I]而不是“I”,编辑它。列表理解…很好+从我这里得到1