Python yield 表达式误区

Python yield expression misunderstanding

提问人:grogor 提问时间:11/16/2023 最后编辑:grogor 更新时间:11/17/2023 访问量:124

问:

我正在阅读这篇文章,我发现那里的以下程序让我感到困惑:

def is_palindrome(num):
    if num // 10 == 0:
        return False
    temp = num
    reversed_num = 0

    while temp != 0:
        reversed_num = (reversed_num * 10) + (temp % 10)
        temp = temp // 10

    if num == reversed_num:
        return True
    else:
        return False

def infinite_palindromes():
    num = 0
    while True:
        if is_palindrome(num):
            print("found a palindrome: "+str(num))
            j = (yield num)
            print("j = "+str(j))
            if j is not None:
                num = j
        print("num = "+str(num))
        num +=1

pal_gen = infinite_palindromes()
for i in pal_gen:
    print("i = "+str(i))
    digits = len(str(i))
    pal_gen.send(10 ** (digits))

输出以以下方式开头:

  • 数字 = 0
  • 数字 = 1
  • 数字 = 2
  • 数字 = 3
  • 数字 = 4
  • 数字 = 5
  • 数字 = 6
  • 数 = 7
  • 数 = 8
  • 数量 = 9
  • 数字 = 10
  • 找到一个回文: 11
  • 我 = 11
  • j = 100
  • 数字 = 100
  • 找到一个回文:101
  • j = 无
  • 数字 = 101
  • 数字 = 102
  • 数字 = 103
  • 数字 = 104
  • 数字 = 105
  • 数字 = 106

我不明白为什么在找到回文:101 后,没有出现 i = 101 和 j = 1000

我的想法: 程序启动。使用 for i in pal_gen:,程序开始在 infinite_palindromes() 中运行 while 循环。当 num=10 且 num += 1 时,if 子句 is_palindrome 被填满。然后,当程序读取 “yield” 时,它与 print(“i = ”+str(i)) 连续。然后,使用 pal_gen.send(10 ** (digits)) 返回生成器的行 j = (yield num) 并将值 100 分配给 j。然后它也分配给 num 100,然后将 num 增加到 101。因为这是一个回文,所以我们再次处于 if 子句中,所以程序打印“found a palindrome: 101”。程序读取并执行的下一行现在又是 j = (yield num)。而且我以为情况和以前一样,所以我想象在读完“yield”之后,程序在print(“i = ”+str(i)))...等等。

python 生成器

评论

0赞 Scott Hunter 11/16/2023
你认为为什么会出现这些?
0赞 Masklinn 11/16/2023
send它不仅将一个值发送到生成器中,它还运行生成器的下一个循环并返回该值。因此,每次发送一个值时,由于您不处理其返回值,您还会在之后删除生成器的第一个值。除了启动生成器的第一次迭代外,您不应混合 / 常规迭代 和 .nextsend
0赞 OM222O 11/17/2023
文章写得很差,这体现在代码质量上。回文代码可以写成:(是的,分号在 Python 中是一回事,我不能在注释中写多行代码)。生成器和循环同样写得很差,所以试着从不同的来源学习生成器。def is_palindrome(x): x_str = str(x); return x_str==x_str[::-1]
0赞 Charles Duffy 11/17/2023
请尝试写一个能独特描述您的问题的标题。我们的知识库中有数百个关于的问题;在搜索结果中查看的人应该如何知道是什么让你与众不同?yield
0赞 grogor 11/17/2023
@Scott猎人。我现在把我的想法包括在内。

答:

1赞 dangoldbj 11/17/2023 #1

在第一次运行 时,pal_gen检测到回文。之后,生成器在语句处暂停,并执行循环的主体。for i in pal_gen:11yield

第二个回文 111 由 检测到 ,这将恢复生成器执行,并将 的值设置为 。 然后生成器在找到回文后再次暂停,并将控制权传回回路。pal_gen.send(10 ** digits)j100111for i in pal_gen:

在循环的第二次运行中,它恢复了生成器的执行,但由于没有向生成器发送任何内容,因此 的值为 。for i in pal_gen:jNone

评论

0赞 grogor 11/17/2023
谢谢。但我仍然不明白。我不明白你的最后一段。我已经编辑了我的问题并写下了我的想法。也许现在更清楚我的困惑究竟在哪里。
0赞 dangoldbj 11/17/2023
@grogor,在循环的第二次运行中,从点继续执行。现在,与上次执行 不同,的值是因为在最后一次执行 时,它被发送了 via 的显式值。但是在这里,在 中,没有发送任何内容,因此 的值是 。for i in pal_gen:pal_genj = (yield num)pal_genjNonepal_gen100pal_gen.send(10 ** digits)for i in pal_gen:jNone
0赞 Masklinn 11/17/2023 #2

问题是您混合了常规迭代和 ,这不起作用:不仅将值注入生成器,还运行整个迭代并返回下一个值sendsend

因此,如果您使用 ,则需要手动执行整个迭代,仅使用 (除了第一次迭代,它必须是 ) 或 和 。sendsendnextnextsend

在这里,因为你混合了发送和常规迭代,所以你跳过了每隔一次迭代:从生成器中提取一个项目,然后被调用,注入一个信号,生成器的下一个值被跳过/删除。send

此脚本显示了问题,第二个代码片段是您在此处执行的操作:

def iterable():
    v = 0
    while v < 42:
        n = yield v
        v = (v if n is None else n) + 1
        
print("regular iteration")
for i in iterable():
    print(i, end=" ")
print()

print("mix iteration and send")
it = iterable()
for i in it:
    print(i, end=" ")
    # you might thing this has no effect since it sends
    # in the same value it got out, but it skips every 
    # other value
    try:
        it.send(i)
    except StopIteration:
        break
print()

print("only send")
it = iterable()
i = next(it)
while True:
    print(i, end=" ")
    # this is the correct way to handle it
    try:
        i = it.send(i)
    except StopIteration:
        break
print()

print("conditional send, mix with next")
it = iterable()
i = next(it)
while True:
    print(i, end=" ")
    # if you mix next and send it's fine as long as
    # you're coherent and store every value
    try:
        if i % 2:
            i = next(it)
        else:
            i = it.send(i)
    except StopIteration:
        break
print()

由于您在每次迭代时都发送,因此您需要执行代码段 3(“仅发送”)。

评论

0赞 grogor 11/18/2023
谢谢!看来你的剧本帮了大忙。第二个代码段是否等同于: it = iterable() i=next(it) print(i) it.send(i) i=next(it) print(i) it.send(i) i=next(it) ....打印(i) ?
0赞 Masklinn 11/18/2023
Kinda,如果不执行隐式,它将是等价的,但它确实如此。sendnext
0赞 grogor 11/20/2023
我认为这正是“它=迭代()i=next(it)打印(i)it.send(i)i=next(it)打印(i)it.send(i)it.send(i)i=next(it)的原因。print(i)“产生与第二个代码段相同的输出?
0赞 Masklinn 11/20/2023
啊,是的,我错误地索引了片段,对不起。没错,循环本质上只是调用并产生它们中的每一个,直到它命中。fornext()StopIteration