Python 2与Python 3 - 过滤器行为的差异

时间:2017-01-15 22:23:05

标签: python python-3.x

有人可以帮助我理解为什么以下代码实现了Eratosthenes"筛选Eratosthenes"在Python 2和Python 3中表现不同。

l = range(2, 20)
for i in range(2, 6):
    l = filter(lambda x: x == i or x % i != 0, l)
print(tuple(l))

使用Python 2.7:

> python filter.py
(2, 3, 5, 7, 11, 13, 17, 19)

使用Python 3.6:

> python filter.py
(2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19)

我知道Python3的过滤器会返回一个过滤器对象但无法解释最终结果。 (代码来自这个lambdas教程1)。

2 个答案:

答案 0 :(得分:7)

在Python-3 filter中返回一个生成器(在Python-2中它返回一个列表),因此在你使用它时会对它进行评估。但这本身并不是一个问题,问题是你的i会发生变化。当您使用filter i = 5 filter以及所有print时,只需检查一下。

我添加了一些l = range(2, 20) for i in range(2, 6): l = filter(lambda x: print(x, i) or (x == i or x % i != 0), l) list(l) 2 5 2 5 2 5 2 5 3 5 3 5 3 5 3 5 4 5 4 5 4 5 4 5 5 5 5 5 5 5 5 5 6 5 6 5 6 5 6 5 7 5 7 5 7 5 7 5 8 5 8 5 8 5 8 5 9 5 9 5 9 5 9 5 10 5 11 5 11 5 11 5 11 5 12 5 12 5 12 5 12 5 13 5 13 5 13 5 13 5 14 5 14 5 14 5 14 5 15 5 16 5 16 5 16 5 16 5 17 5 17 5 17 5 17 5 18 5 18 5 18 5 18 5 19 5 19 5 19 5 19 5 [2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19] 语句,以便您可以更轻松地了解正在发生的事情:

i

这可能不是你的意图。你可以将l = range(2, 20) for i in range(2, 6): l = filter((lambda j: lambda x: print(x, j) or (x == j or x % j != 0))(i), l) # or # l = filter(lambda x, i=i: print(x, i) or (x == i or x % i != 0), l) list(l) 2 2 2 3 2 4 2 5 3 2 3 3 3 4 3 5 4 2 5 2 5 3 5 4 5 5 6 2 7 2 7 3 7 4 7 5 8 2 9 2 9 3 10 2 11 2 11 3 11 4 11 5 12 2 13 2 13 3 13 4 13 5 14 2 15 2 15 3 16 2 17 2 17 3 17 4 17 5 18 2 19 2 19 3 19 4 19 5 [2, 3, 5, 7, 11, 13, 17, 19] 绑定到你的lambda:

filter

或立即将tuple - 结果投射到l = range(2, 20) for i in range(2, 6): l = tuple(filter(lambda x: x == i or x % i != 0, l)) print(l) # (2, 3, 5, 7, 11, 13, 17, 19)

{{1}}

答案 1 :(得分:5)

这里有两个部分发挥作用:

  • 中,filter作为生成器:过滤是懒惰的;和
  • i中的lambda x : ...已更新,i循环中的for也会更新。

所以你最终构建的是:

l = filter(lambda x: x == 5 or x % 5 != 0,
        filter(lambda x: x == 5 or x % 5 != 0,
            filter(lambda x: x == 5 or x % 5 != 0,
                filter(lambda x: x == 5 or x % 5 != 0,l)
            )
        )
    )

请注意,所有过滤都是i始终为5 。所以现在你打电话给tuple(..),实际过滤就会完成,而且你可以看到只有五个不是五个自身的多个被过滤掉了。

一个简单的解决方法是在循环中使用list,以便filter积极完成:

l = range(2, 20)
for i in range(2, 6):
    l = list(filter(lambda x: x == i or x % i != 0, l))
print(tuple(l))

在python中运行它返回:

>>> l = range(2, 20)
>>> for i in range(2, 6):
...     l = list(filter(lambda x: x == i or x % i != 0, l))
... 
>>> print(l)
[2, 3, 5, 7, 11, 13, 17, 19]

请注意,虽然看起来完全相同,但实际上这些"不同"彼此不兼容的语言:运行在一个语言中的代码并不总是在另一个语言中起作用,反之亦然。

另一个注释(归功于@ShadowRanger)是一个实际上可以绑定你的lambda中的i 。你通过创建一个更高阶的lambda"来做到这一点。而不是写:

lambda x : x == i or x % i != 0
你写道:

(lambda j : (lambda x : x == j or x % j != 0))(i)

您定义了一个函数,该函数将实际取值为j的{​​{1}}作为输入。通过立即调用它,i会绑定j的值。