这是我在尝试学习 python 编程时遇到的最常见的问题。问题是,当我尝试使用“range()”函数迭代列表以检查列表中的给定项目是否满足给定条件,如果是则删除它,它总是会给出“IndexError”。那么,有没有一种特殊的方法可以在不使用任何其他中间列表或“while”语句的情况下做到这一点?下面是一个例子:
l = range(20)
for i in range(0,len(l)):
if l[i] == something:
l.pop(i)
这是我在尝试学习 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]
您也可以使用builtin filter()
,尽管在大多数情况下往往不清楚(并且在您需要的地方更慢lambda
)。
另请注意,在 Python 3.x 中,range()
生成的是生成器,而不是列表。
使用更具描述性的变量名称也是一个好主意——例如,我假设这里是这样的,但名称如i
和l
很难阅读,并且更容易引入错误。
编辑:
如果您希望更新现有列表,如评论中所指出的,您可以使用切片语法依次替换列表中的每个项目(l[:] = new_l
)。也就是说,我认为这种情况是非常糟糕的设计。您不希望一段代码依赖以这种方式从另一段代码更新的数据。
编辑2:
如果出于任何原因,您在遍历项目时需要索引,这就是内置enumerate()
函数的用途。
你总是可以用列表理解来做这种事情:
newlist=[i for i in oldlist if not condition ]
您应该从另一面来看问题:当它与“某物”相等时,将一个元素添加到列表中。列表理解:
l = [i for i in xrange(20) if i != something]
for i in range(0,len(l)):
,for i, item in enumerate(l):
如果你需要索引,则使用,for item in l:
如果不需要filter()
或列出推导顺便说一句,在你的情况下,你也可以这样做:
while something in list_: list_.remove(something)
不过,这不是很有效。但根据上下文,它可能更具可读性。
正如其他人所说,遍历列表并创建一个仅包含您想要保留的项目的新列表。
使用切片分配就地更新原始列表。
l[:] = [item for item in l if item != something]
你得到 an 的原因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)
但这不是典型的模式。
与其遍历列表,不如试试 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 中,后者是简短的匿名函数。所以,我们可以将上面的代码重写为...
#-- 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)
试一试,看看它是如何工作的!