以下是Python书籍的副本,关于生成器如何工作的解释对我来说根本不清楚。
gen.send.py
def counter(start=0):
n = start
while True:
result = yield n # A
print(type(result), result) # B
if result == 'Q':
break
n += 1
c = counter()
print(next(c)) # C
print(c.send('Wow!')) # D
print(next(c)) # E
print(c.send('Q')) # F
以上的输出是:
$ python gen.send.py
0
<class 'str'> Wow!
1
<class 'NoneType'> None
2
<class 'str'> Q
Traceback (most recent call last):
File "gen.send.py", line 14, in <module>
print(c.send('Q')) # F
StopIteration
Learning Python. . VitalBook file.
书中的解释:
我们通过调用next(#C)来启动生成器执行。在生成器内,n设置为相同的start值。输入while循环,执行停止(#A),n(0)返回给调用者。控制台上印有0。
#Q1:此时,n = 1,对吗?因为执行打印(类型(结果),结果),所以应该执行n + = 1。
然后我们调用send(#D),执行恢复,结果设置为'哇!' (仍然是#A),然后它的类型和值打印在控制台上(#B)。结果不是'Q',因此n增加1并且执行返回到while条件,其为True,计算结果为True(这不难猜测,对吧?)。另一个循环周期开始,执行再次停止(#A),并且n(1)返回给调用者。 1将打印在控制台上。
Q2:'哇!'发给谁? n,开始还是结果?如何?如果n ='哇!'然后n + = 1的后果是什么?
此时,我们调用next(#E),再次恢复执行(#A),并且因为我们没有显式向生成器发送任何内容,所以Python的行为与不使用return语句的函数完全相同: yield n expression(#A)返回None。
问题3:为什么没有?其值(start,n,result)完全在此生成器中暂停
因此结果设置为None,其类型和值再次打印在控制台上(#B)。执行继续,结果不是'Q'所以n增加1,我们再次开始另一个循环。执行再次停止(#A)并且n(2)返回给调用者。 2在控制台上打印。
问题4:为什么2?为什么不是4或5因为n + = 1语句?
现在为了最后的结局:我们再次调用send(#F),但这次我们传入'Q',因此当执行恢复时,结果被设置为'Q'(#A)。它的类型和值打印在控制台(#B)上,最后if子句的计算结果为True,while循环由break语句停止。生成器自然终止,这意味着引发了StopIteration异常。您可以在控制台上打印的最后几行看到其回溯的打印。
提前致谢。
答案 0 :(得分:1)
解决此类学习问题的一种方法是可视化每一步发生的事情。让我们从头开始:
src
->app folder
main.ts
system.config.js
tsconfig.js
这里没有什么有趣的事情发生。它只是一个函数定义。
def counter(start=0):
n = start
while True:
result = yield n # A
print(type(result), result) # B
if result == 'Q':
break
n += 1
通常它应该执行该功能吗?但由于存在c = counter()
关键字,因此只需返回 yield
即可用于执行该功能。再次阅读上一句话!这是第一个要理解的object
。
generators
这是使用对象print(next(c)) # C
执行函数的方式。您不能使用c
调用它,而是执行()
。这是执行指令的第一次时间,直到找到下一个next(c)
语句为止。由于此时为yield
且A
的值为n
,因此它只打印0
并退出函数 - 最好说函数<强>暂停在这里。记住它甚至没有达到0
!这回答了你的 Q1 。
n +=1
现在发生了一些更有趣的事情。之前停在print(c.send('Wow!')) # D
的生成器c
现在只是恢复,它必须执行的下一个立即指令是yield n
result =
语句result = yield n
}}。 给予您A
的值!所以现在send
刚刚发生。
其余的执行是正常的。当它到达下一个result = 'Wow'
时,它再次出现在函数中。现在yield n
是n
,因为它在n+1
循环中递增。我希望你能猜出代码的其余部分是如何运作的。
while
现在这个语句有些不同,因为它实际上打破了循环的值print(c.send('Q')) # F
,在这种情况下又会阻止任何进一步的sends
。由于生成器不再找到任何yields
语句,因此只会引发yield
异常并停止。如果StopIteration
循环之外有yield
,它会返回并再次暂停。
答案 1 :(得分:0)
Q1:不确定这里的问题是什么
Q2:'Wow!'
已发送至result
。
问题3:result
为None
,因为执行已使用next()
恢复,因此没有任何内容发送到yield表达式。因此,会发送默认值None
。
问题4:为什么要打印4
或5
? n += 1
只执行了两次。
答案 2 :(得分:0)
Q1 :不,此时n
仍为0。发电机在A处停止运行,外部代码在C处打印出第一个产生的值。在将send
作为D行的一部分调用之前,发电机不会开始运行。
Q2 :字符串"Wow!"
成为行A中yield
表达式的值,因此它将被分配给生成器中的result
。它及其类型将在B行打印出来,这是您的第二行输出。然后n
递增,循环重新开始,n
(1
)从c.send
获得返回值。对于第三行输出,它将打印在D行上。
Q3 :您可以通过调用next
来恢复E行上的生成器,这相当于c.send(None)
。因此result
获取值None
(类型为NoneType
),并在生成器代码中打印。
Q4 :我不确定我明白你在这里问的是什么。您似乎了解执行流程。代码永远不会打印更多数字,因为生成器已经结束。它增加n
两次,但在获得Q
后,它会退出。
对于它的价值,你不太可能需要像这个例子那样编写代码。将next
次通话与send
次通话混合起来非常罕见(除了您将在其余时间打电话发送的协程上的第一个next
)。
答案 3 :(得分:0)
将yield
视为特殊的return
语句。当你到达result = yield n
行时,首先执行右边,返回n,即0。与return
的区别在于函数不会停止,它会暂停,所以下次您致电c.send(17)
或next(c)
时,如果您使用{{1},它将从收益中恢复,将其替换为您发送的值(17
)或None
}} 办法。因此,当您第一次调用next(c)
时,它会返回next(c)
并暂停,当您调用0
时,它会继续从生成器内打印类型和值c.send('Wow!')
,然后返回send
暂停,然后继续。
也许如果你将字母添加到print语句中,你可以更容易地看到每个输出行的来源:
1
这将输出:
def counter(start=0):
n = start
while True:
result = yield n # A
print("B:", type(result), result) # B
if result == 'Q':
break
n += 1
c = counter()
print("C:", next(c)) # C
print("D:", c.send('Wow!')) # D
print("E:", next(c)) # E
print("F:", c.send('Q')) # F
回答你的问题:
$ python gen.send.py
C: 0
B: <class 'str'> Wow!
D: 1
B: <class 'NoneType'> None
E: 2
B: <class 'str'> Q
Traceback (most recent call last):
File "gen.send.py", line 14, in <module>
print("F:", c.send('Q')) # F
StopIteration
但在屈服n = 0
后停顿了一下。打印来自n
行。使用print(next(c)) # C
恢复生成器后,n += 1
将执行。c.send('Wow!')
方法的工作方式就好像它替换了生成器暂停的产量,在这种情况下send
- &gt; result = yield n
,因此传递给result = 'Wow!'
。result
时相当于执行next(c)
,因此会恢复执行,取代c.send(None)
(None
- &gt; result = yield n
的收益率result = None
没有到达任何next(c)
; n += 1
达到了一次;第二个c.send('Wow!')
又到达了它;当next(c)
语句执行退出c.send('Q')
循环时break
没有达到它。