提问人:grogor 提问时间:11/16/2023 最后编辑:grogor 更新时间:11/17/2023 访问量:124
Python yield 表达式误区
Python yield expression misunderstanding
问:
我正在阅读这篇文章,我发现那里的以下程序让我感到困惑:
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)))...等等。
答:
在第一次运行 时,pal_gen检测到回文。之后,生成器在语句处暂停,并执行循环的主体。for i in pal_gen:
11
yield
第二个回文 111 由 检测到 ,这将恢复生成器执行,并将 的值设置为 。
然后生成器在找到回文后再次暂停,并将控制权传回回路。pal_gen.send(10 ** digits)
j
100
111
for i in pal_gen:
在循环的第二次运行中,它恢复了生成器的执行,但由于没有向生成器发送任何内容,因此 的值为 。for i in pal_gen:
j
None
评论
for i in pal_gen:
pal_gen
j = (yield num)
pal_gen
j
None
pal_gen
100
pal_gen.send(10 ** digits)
for i in pal_gen:
j
None
问题是您混合了常规迭代和 ,这不起作用:不仅将值注入生成器,还运行整个迭代并返回下一个值。send
send
因此,如果您使用 ,则需要手动执行整个迭代,仅使用 (除了第一次迭代,它必须是 ) 或 和 。send
send
next
next
send
在这里,因为你混合了发送和常规迭代,所以你跳过了每隔一次迭代:从生成器中提取一个项目,然后被调用,注入一个信号,生成器的下一个值被跳过/删除。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(“仅发送”)。
评论
send
next
for
next()
StopIteration
评论
send
它不仅将一个值发送到生成器中,它还运行生成器的下一个循环并返回该值。因此,每次发送一个值时,由于您不处理其返回值,您还会在之后删除生成器的第一个值。除了启动生成器的第一次迭代外,您不应混合 / 常规迭代 和 .next
send
def is_palindrome(x): x_str = str(x); return x_str==x_str[::-1]
yield