You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何理解用埃氏筛生成素数的Python代码中yield from的用法?

埃氏筛惰性生成素数代码解析

核心语法先搞懂:yield与yield from

  • yield是Python生成器的标志,函数只要包含yield就会变成生成器,调用时不会立刻执行代码,每次调用next()才会运行到下一个yield位置,返回对应的值后暂停运行。
  • 你困惑的yield from可以简单理解为生成器的代理转发:yield from x等价于自动遍历x的所有元素,逐个yield出来,和下面的代码效果完全一致:
    for item in x:
        yield item
    
    这里的x可以是任意可迭代对象,包括其他生成器。

逐函数拆解

1. nats(n)函数

def nats(n):
    yield n
    yield from nats(n+1)

这是一个无限生成器,作用是生成从n开始的所有自然数:

  • 调用nats(2)时,第一次next()返回2,之后转发nats(3)的所有输出
  • nats(3)第一次next()返回3,再转发nats(4)的输出,以此类推,要多少就生成多少,不会提前计算所有值。

2. sieve(s)核心筛函数

def sieve(s):
    n = next(s)
    yield n
    yield from sieve(i for i in s if i%n != 0)

这就是埃氏筛的惰性实现,逻辑完全对应埃氏筛的核心规则:每找到一个素数,就筛掉序列中所有它的倍数:

  • 第一步:从输入的序列s中取出第一个数n,这个数一定是素数(因为前面所有更小素数的倍数都已经被筛掉了)
  • 第二步:返回这个素数n
  • 第三步:把输入序列剩下的元素中,所有能被n整除的数过滤掉,用过滤后的新序列再调用sieve,转发新sieve返回的所有素数。

运行逻辑逐次追踪

我们跟着代码里调用next(p)的过程一步步走,就能完全理清:

  1. 初始化p = sieve(nats(2)):此时只是创建了生成器对象,没有执行任何内部代码。
  2. 第一次调用next(p):
    输入序列s是nats(2),next(s)拿到2,返回2后暂停,下一次要从新的sieve(输入是过滤掉所有2倍数的自然数序列:3,5,7,9,11...)拿值。
  3. 第二次调用next(p):
    新sieve的输入序列取第一个值拿到3,返回3后暂停,下一层输入是过滤掉2、3倍数的序列:5,7,11,13,17...
  4. 第三次调用next(p):
    新一层sieve取第一个值拿到5,返回5,下一层输入过滤掉2、3、5倍数的序列,以此类推。

简化理解的小技巧

如果还是觉得递归加yield from绕,可以把所有yield from替换成等价的for循环写法,代码逻辑完全不变但可读性更高:

# 替换后的nats
def nats(n):
    yield n
    for num in nats(n+1):
        yield num

# 替换后的sieve
def sieve(s):
    n = next(s)
    yield n
    for prime in sieve(i for i in s if i%n != 0):
        yield prime

内容的提问来源于stack exchange,提问作者Ibrahim Sherif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:12:03