Kif*_*sif 27 python generator python-3.x
我无法理解这种send方法.我知道它用于操作发电机.但语法在这里:generator.send(value).
我莫名其妙地无法理解为什么值应该成为当前yield表达式的结果.我准备了一个例子:
def gen():
for i in range(10):
X = yield i
if X == 'stop':
break
print("Inside the function " + str(X))
m = gen()
print("1 Outside the function " + str(next(m)) + '\n')
print("2 Outside the function " + str(next(m)) + '\n')
print("3 Outside the function " + str(next(m)) + '\n')
print("4 Outside the function " + str(next(m)) + '\n')
print('\n')
print("Outside the function " + str(m.send(None)) + '\n') # Start generator
print("Outside the function " + str(m.send(77)) + '\n')
print("Outside the function " + str(m.send(88)) + '\n')
#print("Outside the function " + str(m.send('stop')) + '\n')
print("Outside the function " + str(m.send(99)) + '\n')
print("Outside the function " + str(m.send(None)) + '\n')
Run Code Online (Sandbox Code Playgroud)
结果是:
1 Outside the function 0
Inside the function None
2 Outside the function 1
Inside the function None
3 Outside the function 2
Inside the function None
4 Outside the function 3
Inside the function None
Outside the function 4
Inside the function 77
Outside the function 5
Inside the function 88
Outside the function 6
Inside the function 99
Outside the function 7
Inside the function None
Outside the function 8
Run Code Online (Sandbox Code Playgroud)
嗯,坦率地说,这让我很惊讶.
yield执行语句时,生成器的状态被冻结,并且值expression_list返回给next调用者.好吧,似乎没有发生过.为什么我们可以执行if语句和print函数gen().X函数内外不同?好.让我们假设将send(77)77传输到m.好吧,yield表情变成77.然后是什么X = yield i?当外部发生时,函数内部的77如何转换为5?无论如何,你能以某种方式评论这些send和yield陈述吗?
eca*_*mur 53
当你在生成器中使用send和表达yield时,你将它视为一个协程; 一个单独的执行线程,可以顺序交错但不与其调用者并行运行.
当调用者执行时R = m.send(a),它将对象a放入生成器的输入槽,将控制转移到生成器,并等待响应.生成器接收对象a作为结果X = yield i,并运行直到它击中另一个yield表达式,例如Y = yield j.然后它j进入其输出槽,将控制权转移回调用者,并等待它再次恢复.调用者j作为结果接收R = m.send(a),并运行直到它命中另一个S = m.send(b)语句,依此类推.
R = next(m)只是一样的R = m.send(None); 它正在None进入发电机的输入槽,所以如果发电机检查结果X = yield i那么X将是None.
作为一个比喻,考虑一个愚蠢的服务员:

当服务器从客户处获得订单时,他们将垫放在愚蠢的服务员,send将其放入厨房,然后等待孵化器进入菜肴:
R = kitchen.send("Ham omelette, side salad")
Run Code Online (Sandbox Code Playgroud)
厨师(一直在等待舱口)拿起订单,准备菜肴,yield到餐馆,等待下一个订单:
next_order = yield [HamOmelette(), SideSalad()]
Run Code Online (Sandbox Code Playgroud)
服务器(一直在等待孵化器)将菜肴带给顾客并返回另一个订单等.
因为服务器和厨师在send订单或菜单之后等待孵化yield,所以在任何时候只有一个人做任何事情,即过程是单线程的.双方都可以使用正常的控制流程,因为发电机机械(哑服务员)负责交错执行.
xxm*_*jia 20
最令人困惑的部分应该是这条线X = yield i,特别是当你打电话send()给发电机时.实际上你唯一需要知道的是:
在词汇层面:
next()等于send(None)
在解释器级别:
X = yield i等于以下行(订单事项):
yield i
# won't continue until next() or send() is called
# and this is also the entry point of next() or send()
X = the_input_of_send
Run Code Online (Sandbox Code Playgroud)
并且,2行注释是确切的原因,为什么我们需要send(None)第一次调用,因为生成器将在将值赋值之前返回i(yield i)X
Mar*_*anD 14
注意: \n为了简单起见,我的答案仅限于生成器每行
最多有 1 个命令的情况。yield
长话短说:
\n方法.send():
yield(唤醒它),但是yield。方法发送的值的接收者.send()是yield 表达式本身。
\n这意味着表达式yield 7
7,但是(yield 7),可以是例如 "hello"yield 7) \xe2\x80\x94 如果该命令是通过方法唤醒的.send("hello")。第一个send(带有None参数)启动生成器实例,因此它开始执行其命令。
前言:
\n让g = gen()ieg是生成器迭代器的一个实例gen()
\n(下图右侧)。
该命令的next(g)行为与 完全相同 g.send(None),因此您可以使用您喜欢的任何一个。
仅当实例在使用以下命令的语句中挂起时才允许发送非None值: \ngyield
.send()发送值(请参见下面 \xe2\x80\x9cStep by step\xe2\x80\x9d 部分中的第4点)。yield因此,在发送非值之前None,我们必须通过向生成器实例发送值来将其置于挂起状态None。它可能很简单g.send(None):
但就在g暂停之前,它会产生命令的值yield。这个产生的值成为该方法的返回值.send():
我们可能想使用这个接收到的值或将其保存在变量中以供以后使用,因此我们不使用前面的两张图片,而是以此开始我们的旅程:
\n一步步:
\n第一个.send()启动实例g。实例g开始执行其命令,直到第一个yield语句,该语句产生其值:
这意味着变量中将from_iterator_1是 string "first_from_iterator"。
\xc2\xa0
\n现在,在产生第一个值之后,我们处于g暂停状态
这允许我们发送到除\xe2\x80\x94之外的有用的g东西,例如数字。None1
\xc2\xa0
\n\xc2\xa0
\n由于在表达式g处挂起,\n该表达式(本身)的值 将变为。 yield "first_from_iterator"1
(是的,theyield "first_from_iterator" 是一个表达式,同样a + b也是。)
回想一下,此时此刻的价值"first_from_iterator"早就已经产生了。
\xc2\xa0
\n然后实例g被唤醒,\xe2\x80\x94 依次 \xe2\x80\x94 现在g.send()等待返回值。\n
\xc2\xa0
\n先前挂起的、现在唤醒的语句将被执行。
\n(挂起之前,并没有执行,只是产生了一个值。 )\n
\n在我们的简单情况下(唤醒语句是yield "first_from_iterator"),仍然没有任何需要执行的操作,但是呢
将接收到的值 ( 1) 保存到变量以供以后使用?
received_1 = yield "first_from_iterator" \nRun Code Online (Sandbox Code Playgroud)\n或者用它来执行更复杂的计算?
\nresult = 3 * (yield "first_from_iterator") + 2 # result: 5\nRun Code Online (Sandbox Code Playgroud)\n\xc2\xa0
\n中的所有后续语句都g将被执行,但仅限于包含该yield命令的下一条语句。
\xc2\xa0
\n下一条语句(其中包含yield命令)会产生一个值
它g再次挂起,并唤醒等待.send()方法(通过向其提供等待的 \xe2\x80\x94 产生 \xe2\x80\x94 返回值)。
\xc2\xa0
\n它允许在其之后执行下一个命令:
\n\n\xc2\xa0
\n现在我们处于与第 2 点相同的情况。 \xe2\x80\x94 就在执行(下一个).send()方法 \xe2\x80\x94 之前,因此故事将重复。
注意:
\n它将重复与上面 \xe2\x80\x9cPreface\xe2\x80\x9d 部分的最后一点相同的问题\xe2\x80\x94 我们可能不想丢弃生成的结果值,所以代替命令
g.send(1) # Not very appropriate\nRun Code Online (Sandbox Code Playgroud)\n最好使用一些东西作为
\nfrom_iterator_2 = g.send(1) # Saving the 2nd yielded value\nRun Code Online (Sandbox Code Playgroud)\n(对于下一个命令也是如此g.send(2))。
小智 6
def gen():
i = 1
while True:
i += 1
x = yield i
print(x)
m = gen()
next(m)
next(m)
m.send(4)
Run Code Online (Sandbox Code Playgroud)
结果
None
4
Run Code Online (Sandbox Code Playgroud)
看看上面更简化的代码.
我认为引起你困惑的事情是'x = yield i'声明,这个声明并没有说从send()方法接受的值,然后我把它命名为x.相反,值i由yield statment返回到生成器,x由send()方法确定.一个语句同时做两件事.