Python:我不知道';我不明白是什么';这台发电机怎么了
我很好奇这里发生了什么事。熟悉生成器和协同程序的人能很好地解释这段代码吗Python:我不知道';我不明白是什么';这台发电机怎么了,python,generator,coroutine,Python,Generator,Coroutine,我很好奇这里发生了什么事。熟悉生成器和协同程序的人能很好地解释这段代码吗 def b(): for i in range(5): yield i x = (yield) print(x) def a(): g = b() next(g) for i in range(4): g.send(5) print(next(g)) a() 输出 None 1 None 2 None 3
def b():
for i in range(5):
yield i
x = (yield)
print(x)
def a():
g = b()
next(g)
for i in range(4):
g.send(5)
print(next(g))
a()
输出
None
1
None
2
None
3
None
4
但是,当我切换到第3行和第4行时:yield I
和x=(yield)
,我得到了以下结果
5
None
5
None
5
None
5
None
我怀疑问题可能是因为我试图使用yield语句在同一个函数中同时接收和发送值。这在Python中是不可能的吗
我已经成功地编写了两个使用协同程序的程序,因此我熟悉它们的工作方式,但我对这段代码的行为方式感到困惑。对此有任何见解都将不胜感激
谢谢
编辑:感谢布伦巴恩和联合国大学的回答。当你把问题扩展成这样时,这里发生的事情就更有意义了
def b():
for i in range(5):
yield i
x = yield None
def a():
g = b()
print('* got', g.send(None) )
for i in range(4):
print('+ got', g.send(5) )
print('- got', g.send(None))
a()
我不太明白您的要求,但基本上:当您使用
send
时,它会使生成器中最近达到的产量表达式计算为您发送的值。还要注意的是,send
将生成器推进到下一个yield
。有一件事可能会让您感到困惑:您正在生成器内部打印x
的值,您正在b
内部打印next(g)
的值,但是生成器也在g.send(5)
处生成值,而您没有打印这些值
在第一种情况下,您的第一个send
会导致yield i
语句在b
中计算为5,但在b
中不使用此值(您不会将yield i
分配给任何对象),因此它不会执行任何操作。另外,当您执行send(5)
时,生成器将不产生任何结果(从x=(yield)
行),但您不打印它,因此您不知道这一点。然后使用next(g)
再次推进发电机。最近达到的收益率是x=yield
,但是next(g)
没有传递任何值,因此x
被设置为无
在第二种情况下,调用的奇偶性是相反的。现在,您的第一个send
确实发送到x=yield
行,因此x
设置为5。此send
也会在b
中生成循环值,但在a
中忽略此值,不打印它。然后打印next(g)
,该值为无。在每次后续发送时,b
打印x
的值,该值始终为5,因为您总是发送该值,然后a
打印下一个生成的值,该值始终为零(因为这是x=yield
生成的值)
我不太明白你所说的“使用yield语句在同一个函数中接收和发送值”是什么意思。您当然可以这样做,但您必须意识到:a)即使在调用
next(g)
时,仍然会发送一个值(None);b)当您使用traceit
逐行通过程序调用g.send(5)
时,仍然会生成一个值:
import sys
import linecache
class SetTrace(object):
'''
with SetTrace(monitor):
...
'''
def __init__(self, func):
self.func = func
def __enter__(self):
sys.settrace(self.func)
return self
def passit(self, frame, event, arg):
return self.passit
def __exit__(self, ext_type, exc_value, traceback):
sys.settrace(self.passit)
def traceit(frame, event, arg):
'''
http://www.dalkescientific.com/writings/diary/archive/2005/04/20/tracing_python_code.html
'''
if event == "line":
lineno = frame.f_lineno
filename = frame.f_globals["__file__"]
if (filename.endswith(".pyc") or
filename.endswith(".pyo")):
filename = filename[:-1]
name = frame.f_globals["__name__"]
line = linecache.getline(filename, lineno)
print("%s # %s:%s" % (line.rstrip(), name, lineno, ))
return traceit
def b():
for i in range(5):
yield i
x = (yield)
print(x)
def a():
g = b()
next(g)
for i in range(4):
g.send(5)
print(next(g))
with SetTrace(traceit):
a()
我们获得
g = b() # __main__:44
next(g) # __main__:45 # runs b until you get to a yield
for i in range(5): # __main__:38
yield i # __main__:39 # stop before the yield; resume a
^
for i in range(4): # __main__:46
g.send(5) # __main__:47 # resume b; (yield i) expression evals to 5 then thrown away
x = (yield) # __main__:40 # stop before yield; resume a
^
print(next(g)) # __main__:48 # next(g) called; resume b; print not called yet
print(x) # __main__:41 # next(g) causes (yield) to evaluate to None
None
for i in range(5): # __main__:38
yield i # __main__:39 # yield 1; resume a; `print(next(g))` prints 1
1
for i in range(4): # __main__:46
g.send(5) # __main__:47 # resume b; (yield i) expression evals to 5 then thrown away
右侧(上面)的注释解释了为什么Python先打印None
,然后打印1
。如果你做到了这一点,我想很清楚为什么你会得到None
,2
,等等——同样的故事又一次发生了,I
的值不同
另一种情况下,
x=(收益率)
和收益率i
是相反的,可以进行类似的分析。谢谢,正如你所说,我很困惑,因为我没有打印g.send(5)
的结果。我还忘记了x=yield
与x=yield None
是一样的。