我很好奇这里发生了什么。知道生成器和协同程序的人可以很好地解释这段代码。
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中不可能吗?
我已经成功编写了几个使用协同程序的程序,因此我对它们的工作方式很熟悉,但我对这段代码的表现方式感到困惑。任何有关这方面的见解将不胜感激。
由于
编辑:感谢BrenBarn和unutbu为您的答案。当你将问题扩展出来时,这里发生的事情更有意义。
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()
答案 0 :(得分:5)
使用traceit
逐行逐步完成该程序:
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 = (yield)
和yield i
相反的另一种情况。
答案 1 :(得分:4)
我不太了解你的问题,但基本上:当你使用send
时,它会导致生成器中最近达到的yield表达式计算为你发送的值。另请注意,send
会将生成器前进到下一个yield
。可能令您感到困惑的一件事是您在生成器中打印x
的值,并且您在next(g)
内打印b
的值,但生成器也在产生值在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
打印 next 产生的值,总是无(因为这是x = yield
产生的)。
我不太明白你的意思是“使用yield语句来接收和发送同一个函数中的值”。你当然可以做到这一点,但你必须意识到:a)即使你打电话给next(g)
,仍然会发送一个值(无);和b)当你致电g.send(5)
时,仍会产生一个价值。