Python过滤器无法正常工作。

8

我有一个算法可以生成质数列表作为生成器:

def _odd_iter():
    n=3
    while True:
        yield n
        n=n+2

def _not_divisible(n):
    return lambda x: x % n > 0

def primes():
    yield 2
    L=_odd_iter()
    while True:
        n=next(L)
        yield n
        L=filter(_not_divisible(n), L)

x=1
for t in primes():
    print(t)
    x=x+1
    if x==10:
        break

但是如果我直接将lambda函数放入filter函数中,例如下面:

def primes():
    yield 2
    L=_odd_iter()
    while True:
        n=next(L)
        yield n
        L=filter(lambda x: x%n>0, L)

我只能得到奇数列表,而不是质数列表。似乎filter函数不起作用。

我该怎么办?


2
你尝试过将它改为 lambda x=x: ... 吗? - TigerhawkT3
1
这真的很奇怪。 - Trevor Merrifield
算法的第一个版本对我完全不起作用。以下代码:_ = primes(); print next(_); print next(_); print next(_); 打印出 2,然后是 3,然后就卡住了。你使用的是哪个版本的Python? - A. Vidor
2
它适用于Python 3,但不适用于2。 - Trevor Merrifield
第一个版本在我的Python 3.50上运行良好。有人在Python 2上遇到错误吗?我无法在我的当前机器上检查它。 - juanpa.arrivillaga
请查看以下链接:https://dev59.com/9XE95IYBdhLWcg3wd9xK - Tonechas
2个回答

8
这是一个更简单的程序,它展示了同样的问题。
adders = []
for i in range(4):
    adders.append(lambda a: i + a)
print(adders[0](3))

虽然我们期望输出结果是3,但实际输出结果是6。这是因为在Python中闭包会记住变量的名称和范围,而不是lambda创建时的值。由于i在lambda使用时已被修改,所以lambda使用的是i的最新值。
在您的函数中也发生了同样的事情。每当修改n时,各个过滤器中的所有lambda函数也会被修改。因此,当迭代器到达9时,所有过滤器都会过滤7的因数,而不是53的因数。
由于在第一种方法中每次调用_not_divisible时都创建了一个新的范围,所以函数按预期工作。
如果您一定要直接使用lambda,可以使用第二个参数,例如:
def primes():
    yield 2
    L=_odd_iter()
    while True:
        n=next(L)
        yield n
        L=filter(lambda x, n=n: x%n>0, L)

4
有效的lambda表达式是lambda x, n=n: x%n != 0。如果想要在定义lambda时捕获n,则需要这样做。否则,lambda只会在评估lambda表达式时查找变量名。在您的情况下,我认为这意味着在while循环的后续迭代中锁定了一个n值。

Python 2和Python 3中闭包的工作方式有什么区别? - juanpa.arrivillaga
1
@juanpa.arrivillaga 没有区别... 但是这段代码依赖于Python 3,因为在Python 3中,filter返回一个生成器而不是计算值。 - donkopotamus
@donkopotamus 当然!你应该把那个作为答案发布。 - juanpa.arrivillaga
@donkopotamus 不用在意,实际上,我一开始就误读了问题。 - juanpa.arrivillaga
1
谢谢@Trevor,你的回答也非常有帮助。 - Wei Tang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接