lur*_*x66 1 python yield generator
在阅读了文档、问题并制作了我自己的测试代码后,我相信我已经理解了 ayield expression是如何工作的。
尽管如此,我对以下示例代码的行为感到惊讶:
def gen(n=0):
while True:
n = (yield n) or n+1
g=gen()
print( next(g) )
print( next(g) )
print( g.send(5) )
print( next(g) )
print( next(g) )
Run Code Online (Sandbox Code Playgroud)
我原以为它会返回 0, 1, 2, 5, 6,而它会产生:0, 1, 5, 6, 7。
即:我原以为会yield expression产生这些效果:
yield expression,并将其返回给调用者send()并将它们用作生成器函数代码接收的 yield 表达式的值next(g)或g.send()调用...和/或 Python 会注意避免 (1) 和 (2) 中的两个信息流之间的任何干扰,即保证它们是独立的,例如在元组分配中 a, b = f(a,b), g(a,b)
(我什至想知道在 (1) 和 (2) 之间进行暂停是否更好,但也许它会非常复杂,因为这意味着仅执行部分语句,其余部分保留用于下一份简历)
无论如何,操作的顺序是(2),然后(1),然后(3),因此(2)中的赋值发生在之前,并且可以影响(1)中的赋值。即g.send()调用注入的值是在计算yield 表达式本身之前使用的,它作为相同g.send()表达式的值直接暴露给调用者。
我很惊讶,因为从生成器表达式中的代码的角度来看,它接收到的值lhs会影响rhs!
对我来说,这有点误导,因为人们期望在像 那样的语句中lhs expr = rhs expr, 中的所有计算在rhs expr执行分配之前完成,并在分配期间冻结。lhs一个作业的 可以影响它自己的看起来真的很奇怪rhs!
问题是:它以这种方式制造的原因是什么?有什么线索吗?
(我知道“我们更喜欢可以回答的问题,而不仅仅是讨论”,但这是我绊倒并让我消耗大量时间的事情。我相信一点讨论不会有任何坏处,也许会节省别人的时间)
附注。当然我明白我可以将分配分成两个步骤,这样从接收到的任何值send()将仅在恢复操作后使用。像这样:
def gen(n=0):
while True:
received = (yield n)
n = received or (n+1)
Run Code Online (Sandbox Code Playgroud)
你的困惑在于generator.send()。发送与使用相同next(),区别在于yield表达式产生不同的值。换句话说,next(g)与 相同g.send(None),两个操作都在那里恢复生成器。
请记住,生成器在顶部开始暂停。第一次next()调用前进到第一个yield表达式,停止生成器然后暂停。当一个yield表达被暂停和你打电话或者 next(g)或者g.send(..),发电机恢复它在哪里,现在,然后运行,直到下一个yield到达的表情,此时它再次暂停。
对于您的代码,会发生这种情况:
g 被创建,什么都没有发生 gen()next(g)实际上进入函数体,n = 0被执行,yield n暂停g和产生0。这是打印出来的。next(g)恢复发电机;None返回yield n(毕竟没有发送任何内容),因此None or n + 1执行n = 1并设置。循环继续并yield n再次到达,生成器暂停并1产生。这是打印出来的。g.send(5)恢复发电机。5 or n + 1手段n = 5被执行。循环继续,直到yield n到达,生成器暂停,5产生并打印5。next(g)恢复发电机;None返回(没有再次发送任何内容),因此None or n + 1执行n = 6并设置。循环继续并yield n再次到达,生成器暂停并6产生并打印。next(g)恢复发电机;None返回(没有再次发送任何内容),因此None or n + 1执行n = 7并设置。循环继续并yield n再次到达,生成器暂停并7产生并打印。鉴于您的步骤 1., 2. 和 3.,实际顺序是 3., 2., 1. 然后,添加next()也经过步骤2.生产None,并且1.是在取消暂停后遇到的下一次调用yield。
| 归档时间: |
|
| 查看次数: |
226 次 |
| 最近记录: |