编写Python素数打印脚本时a.py与b.py输出差异原因探究
问题描述
我正在编写一个打印素数的Python脚本,以下是两个仅变量名不同的脚本,输出却存在差异:
a.py
def all(): n = 2 while True: yield n n = n + 1 y = all() number = next(y) print(number) y = filter(lambda x: x % number != 0, y) number = next(y) print(number) y = filter(lambda x: x%3 != 0, y) print(next(y)) y = filter(lambda x: x%5 != 0, y) print(next(y)) y = filter(lambda x: x%7 != 0, y)
输出:2 3 4 7
b.py
def all(): n = 2 while True: yield n n = n + 1 y = all() number = next(y) print(number) y = filter(lambda x: x % number != 0, y) num = next(y) print(num) y = filter(lambda x: x%3 != 0, y) print(next(y)) y = filter(lambda x: x%5 != 0, y) print(next(y)) y = filter(lambda x: x%7 != 0, y)
输出:2 3 5 7
明明变量num和number在y = filter(lambda x: x%3 != 0, y)之后从未被使用,为何该语句之后的输出会出现差异?
解答
这是Python闭包的延迟绑定特性导致的:lambda里引用的变量,不是定义lambda时的固定值,而是等到lambda被实际调用时,才去读取变量的当前值。
拆解a.py的问题:
- 创建第一个
filter时,lambda x: x % number != 0并没有把当时number的数值2存下来,只是记录了要引用number这个变量。 - 紧接着你执行
number = next(y),把number的值改成了3。 - 后面当第一个
filter开始真正工作(调用next(y)触发迭代时),lambda才去取number的值,这时候已经是3了。原本想让第一个过滤掉所有偶数的逻辑,变成了过滤掉所有能被3整除的数。 - 所以第三个
next(y)拿到的是4:4能通过第一个filter(4%3≠0),也能通过第二个filter(4%3≠0),最终输出4。
再看b.py的正确逻辑:
你用了新变量num来接收next(y)的结果,原来的number变量一直保持初始值2。
当第一个filter开始工作时,lambda读取到的number还是2,正确过滤掉了所有偶数。
第三个next(y)时,生成器跳过了被第一个filter过滤的偶数4,直接拿到5,5能通过第二个filter(5%3≠0),所以输出5。
本质就是a.py复用number变量时,无意间修改了第一个lambda依赖的变量值,而b.py用新变量保留了原变量的初始值,导致两个脚本的过滤逻辑产生差异,最终输出不同。
内容的提问来源于stack exchange,提问作者user1602200
相关产品推荐
相关产品推荐

