2

我正在尝试过滤一些我正在使用的数据,以去除我的测量设备中的一些伪影,例如负数和错误。我一直在玩使用生成器来做到这一点的想法。我正在使用 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

4 回答 4

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]
于 2012-06-06T20:38:17.763 回答
1

您正在修改您正在处理的列表,这有点类似于在某些其他语言中从循环内部修改例如 for 循环的索引值。考虑将此方法作为替代方法:

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

使用列表推导应该更直接地工作,虽然它不是生成器表达式,但可以简单地改为一个:

testlist = (x for x in testlist if x >= 0 and x <= 2.5)
于 2012-06-06T20:39:03.597 回答
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)
于 2012-06-06T20:41:31.727 回答
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]
于 2012-06-06T20:51:58.920 回答