本问题已经有最佳答案,请猛点这里访问。
这是我在学习Python编程时遇到的最常见的问题。问题是,当我尝试使用"range()"函数迭代列表时,检查列表中的给定项是否满足给定条件,如果满足,则删除它时,它将始终给出"indexError"。那么,有没有一种特殊的方法可以做到这一点而不使用任何其他中间列表或"while"语句呢?下面是一个例子:
l = range(20)
for i in range(0,len(l)):
if l[i] == something:
l.pop(i)
检查过滤器内置功能。这就是你想要的。
为什么要在条件为i==的地方编写这样的代码?在这种情况下,我不是一个非常有用的比较项目
@你是赖特,我的意思是"我"。
首先,您永远不想在Python中迭代类似的事情。迭代实际对象,而不是索引:
l = range(20)
for i in l:
...
出现错误的原因是您正在删除一个项,因此以后的索引将不再存在。
现在,您不能在循环遍历列表时修改列表,但这不是问题。更好的解决方案是在这里使用列表理解,过滤掉多余的项目。
l = range(20)
new_l = [i for i in l if not i == something]
号
您也可以使用filter()内置,尽管在大多数情况下这一点往往不明确(并且在需要lambda的地方会变慢)。
还要注意,在python 3.x中,range()生成一个生成器,而不是一个列表。
使用更具描述性的变量名也是一个好主意——我在这里假设是这样的,但是像i和l这样的名称很难阅读,并且更容易引入错误。
编辑:
如果您希望更新现有列表,如注释中所指出的那样,您可以使用切片语法依次替换列表的每个项(l[:] = new_l)。也就是说,我认为这个案子设计得很糟糕。您不希望一段代码依赖于以这种方式从另一位代码更新的数据。
编辑2:
如果出于任何原因,在循环项目时需要索引,这就是enumerate()内置的目的。
这假设您只有一个引用列表的名称。
@如果你愿意的话,你可以把新的名单重新分配给旧的。
如果代码中有其他不受您直接控制的引用,则不会。我知道有点牵强,但有时需要就地修改。
@所以在这种情况下,使用一个切片分配来更新列表。
@然后,可以使用切片语法依次替换列表中的每个项(l[:] = new_l)。也就是说,我认为这个案子设计得很糟糕。您不希望一段代码依赖于以这种方式从另一位代码更新的数据。
@米吉尔森就是这样。谢谢。
@我是这样想的吗,但如果有时间我需要索引号来引用列表中与当前项目相关的其他项目呢?
@我不知道你的意思。那个问题似乎与我的观点无关。
@alinwndrld如果在循环遍历列表时需要列表中某个项的索引,请使用enumerate()内置项。
@戴维德费尔南我是说拿铁咖啡。
@你可以按你的要求去做,但是你需要按我想的相反的顺序循环列表。
@戴维汉弗曼,有什么原因可以反过来循环列表?
因此,您可以从列表中删除项目,而不必破坏迭代。
@戴维德·费弗曼在迭代列表时修改列表是个坏主意,而且很脆弱。使用列表理解(或者需要时使用生成器表达式)是更好的解决方案。
我们正在绕圈子。如果创建一个新的列表对象是可以接受的,那么您所说的是正确的。如果你必须在适当的地方修改,那么反过来浏览列表是一个古老的、值得尊敬的技巧。
没有理由相信在这里创建一个新列表是一个问题,如果是的话,使用生成器表达式(因此一次只计算一个项目)是比反向遍历列表更好的解决方案。
生成器表达式如何修改列表对象?
它没有,但这意味着你不需要创建一个新的列表,所以你不需要比现有列表所用的内存更多的内存-唯一需要修改的其他原因是一个糟糕的列表。
你可以用列表理解来做这类事情:
newlist=[i for i in oldlist if not condition ]
列表理解…很好。+我的1个
获取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.
。
还要注意,您是根据列表的索引做出"pop"决定的,而不是列表的索引单元格中的内容。这是不寻常的——这是你的意图吗?还是你?意思是更像…
if l[i] == something:
l.pop(i)
现在,在您的具体示例中,(l[i] == i)但是这不是一个典型的模式。
尝试使用filter函数,而不是遍历列表。它是一个内置的(就像许多其他列表处理功能一样:例如映射、排序、反转、压缩等等)。
试试这个…
#-- 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比这里的列表理解慢且不太清楚。
@拿铁是同意的。我在上面列出了清单理解解决方案。在这种情况下,我唯一要注意的是,对于许多初学者来说,列表理解实际上不如更详细的方法清楚。否则,我完全赞成。
就像所有的语言结构一样,它们在被使用之前需要被解释,是的,但是我不认为这意味着它们应该永远被避免。
是的,我的意思是"我"而不是"我",编辑了它。
正如其他人所说,迭代列表并创建一个只包含您想要保留的项的新列表。
使用切片分配就地更新原始列表。
l[:] = [item for item in l if item != something]
。
不应使用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)
不过,这不是很有效。但根据上下文,它可能更可读。
你应该从另一个角度来看待这个问题:当一个元素与"something"相等时,将它添加到一个列表中。列表理解:
l = [i for i in xrange(20) if i != something]
。