Python 3、yield 表达式return value 受其刚刚通过send() 收到的值的影响?
Python 3, yield expression return value influenced by its value just received via send()?
阅读文档、问题并编写自己的测试代码后,我相信我已经理解了 yield 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) )
我原以为它 returned 0, 1, 2, 5, 6,而它却产生:0, 1, 5, 6, 7.
即:我本以为 yield expression
会产生这些效果:
- 计算
yield expression
的值,并将其 return 返回给调用者
- 从调用者的
send()
中获取值并将它们用作生成器函数代码接收的 yield 表达式的值
- 在执行任何其他操作之前暂停执行;它将在相同的
next(g)
或 g.send()
调用 的相同点恢复
... and/or Python 会注意避免两者之间的任何干扰
(1) 和 (2) 中的信息流,即保证它们是独立的,例如在元组赋值 a, b = f(a,b), g(a,b)
中
(我什至想知道是否让暂停发生在(1)和(2)之间更好,但也许它会非常复杂,因为它意味着只有部分语句被执行并且休息留到下一份简历)
反正运算的顺序是(2),然后(1),再(3),所以(2)的赋值发生在(2)之前,可以影响(1)的赋值。 IE。 g.send()
调用注入的值在计算 yield 表达式本身之前使用,它作为相同 g.send()
表达式的值直接暴露给调用者。
我很惊讶,因为从生成器表达式中的代码的角度来看,在其 lhs
中接收的值会影响 rhs
!
所接收的值
对我来说,这是一种误导,因为人们期望在像 lhs expr = rhs expr
这样的语句中,rhs expr
中的所有计算都在执行赋值之前完成,并在赋值期间冻结。一个赋值的 lhs
可以影响它自己的 rhs
看起来真的很奇怪!
问题:为什么会这样?有什么线索吗?
(我知道"We prefer questions that can be answered, not just discussed",但这是我偶然发现的事情,让我浪费了很多时间。我相信一些讨论不会有任何坏处,也许会拯救别人的时间)
PS。当然我明白我可以把赋值分成两步,这样从 send()
收到的任何值都只会在恢复操作后使用。像这样:
def gen(n=0):
while True:
received = (yield n)
n = received or (n+1)
您的困惑在于 generator.send()
。发送 与使用 next()
相同,区别在于 yield
表达式产生不同的值。换句话说,next(g)
与 g.send(None)
是一回事,这两个操作都会立即恢复生成器。
请记住,生成器 starts paused,位于顶部。第一个 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
在取消暂停后遇到。
阅读文档、问题并编写自己的测试代码后,我相信我已经理解了 yield 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) )
我原以为它 returned 0, 1, 2, 5, 6,而它却产生:0, 1, 5, 6, 7.
即:我本以为 yield expression
会产生这些效果:
- 计算
yield expression
的值,并将其 return 返回给调用者 - 从调用者的
send()
中获取值并将它们用作生成器函数代码接收的 yield 表达式的值 - 在执行任何其他操作之前暂停执行;它将在相同的
next(g)
或g.send()
调用 的相同点恢复
... and/or Python 会注意避免两者之间的任何干扰
(1) 和 (2) 中的信息流,即保证它们是独立的,例如在元组赋值 a, b = f(a,b), g(a,b)
(我什至想知道是否让暂停发生在(1)和(2)之间更好,但也许它会非常复杂,因为它意味着只有部分语句被执行并且休息留到下一份简历)
反正运算的顺序是(2),然后(1),再(3),所以(2)的赋值发生在(2)之前,可以影响(1)的赋值。 IE。 g.send()
调用注入的值在计算 yield 表达式本身之前使用,它作为相同 g.send()
表达式的值直接暴露给调用者。
我很惊讶,因为从生成器表达式中的代码的角度来看,在其 lhs
中接收的值会影响 rhs
!
对我来说,这是一种误导,因为人们期望在像 lhs expr = rhs expr
这样的语句中,rhs expr
中的所有计算都在执行赋值之前完成,并在赋值期间冻结。一个赋值的 lhs
可以影响它自己的 rhs
看起来真的很奇怪!
问题:为什么会这样?有什么线索吗?
(我知道"We prefer questions that can be answered, not just discussed",但这是我偶然发现的事情,让我浪费了很多时间。我相信一些讨论不会有任何坏处,也许会拯救别人的时间)
PS。当然我明白我可以把赋值分成两步,这样从 send()
收到的任何值都只会在恢复操作后使用。像这样:
def gen(n=0):
while True:
received = (yield n)
n = received or (n+1)
您的困惑在于 generator.send()
。发送 与使用 next()
相同,区别在于 yield
表达式产生不同的值。换句话说,next(g)
与 g.send(None)
是一回事,这两个操作都会立即恢复生成器。
请记住,生成器 starts paused,位于顶部。第一个 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
在取消暂停后遇到。