使用生成器表达式后出现意外结果

时间:2012-06-06 20:35:41

标签: python

我正在尝试过滤一些我正在使用的数据,以便在我的测量设备中删除一些伪像,例如负数和错误。我一直在玩使用发电机这样做的想法。我使用的是Python 2.7.2

testlist = [12,2,1,1,1,0,-3,-3,-1]  

gen = (i for i, x in enumerate(testlist) if x < 0 or x > 2.5)

for i in gen: testlist.pop(i)

print testlist

返回:

[2, 1, 1, 1, 0, -3]

我的问题是为什么-3值出现在更新的“测试列表”中?

4 个答案:

答案 0 :(得分:7)

当您从列表中删除项目时,项目更改后的索引(它们都向下移动一个)。结果,生成器将跳过一些项目。尝试添加一些打印语句,以便您可以看到正在发生的事情:

for i in gen:
        print i
        print testlist
        testlist.pop(i)

输出:

0
[12, 2, 1, 1, 1, 0, -3, -3, -1]
5
[2, 1, 1, 1, 0, -3, -3, -1]
6
[2, 1, 1, 1, 0, -3, -1]

您需要删除索引0,5,5,5处的项目。生成器生成索引0,5,6。这是有道理的,因为enumerate返回0, 1, 2, ...等。它赢了不会连续两次返回相同的索引。

一次删除一个元素也非常低效。这需要多次移动数据,最坏情况下的性能为O(n 2 )。您可以使用列表推导。

testlist = [x for x in testlist if 0 <= x <= 2.5]

答案 1 :(得分:1)

您正在修改您正在处理的列表,有点类似于修改索引值,例如,来自 in 循环的for循环,在某些其他语言中。将此方法视为替代方案:

testlist = [x for x in testlist if x >= 0 and x <= 2.5]

使用list comprehension应该更直接地工作,虽然它不是generator expression,但可以简单地改为1:

testlist = (x for x in testlist if x >= 0 and x <= 2.5)

答案 2 :(得分:1)

让我们考虑一个更简单的输入:

[-3, -4, -5]

首先(0,-3)取自枚举器。 0被添加到生成器。 for循环注意到生成器中有一个新元素,并删除-3:

[-4, -5]

从枚举器中获取一个新元素。枚举器会记住第一个元素,所以现在它将采用第二个元素:-5。 -5以相同的方式从列表中删除。 -4仍然存在。

顺便说一句,一个更简单的方法来做你正在尝试的事情如下:

testlist = filter(lambda x: x >= 0 and x <= 2.5, testlist)

答案 3 :(得分:1)

更好的方法是使用列表推导来创建新的过滤列表:

testlist = [12,2,1,1,1,0,-3,-3,-1]  

testlist[:] = [x for x in testlist if 0 <= x <= 2.5]

,并提供:

[2, 1, 1, 1, 0]