我无法理解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')
结果是:
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
嗯,坦率地说,这让我感到惊讶。
yield
语句时,生成器的状态被冻结,expression_list
的值返回给next
的调用者。
好吧,似乎没有发生过。为什么我们可以在if
内执行print
语句和gen()
函数。 X
不同?
好。我们假设send(77)
将77传输到m
。好吧,yield
表达式变为77。
那么X = yield i
是什么?当外部发生时,函数内部的77如何转换为5?无论如何,你能以某种方式评论这些send
和yield
陈述吗?
答案 0 :(得分:51)
在生成器中使用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
。
作为一个比喻,考虑dumb waiter:
当服务器从客户处获得订单时,他们将垫放在愚蠢的服务员中,send
将它放到厨房,然后等待孵化器播放:
R = kitchen.send("Ham omelette, side salad")
厨师(他一直在等舱)拿起订单,准备菜肴,yield
到餐馆,然后等待下一个订单:
next_order = yield [HamOmelette(), SideSalad()]
服务员(一直在等待的人)将菜肴带给顾客并返回另一个订单等。
因为服务员和厨师在send
订单或yield
菜单之后等待孵化,所以在任何时候只有一个人做任何事情,即该过程是单线程的。双方都可以使用正常的控制流程,因为发电机械(哑服务员)负责交错执行。
答案 1 :(得分:18)
最令人困惑的部分应该是此行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
并且,2行注释是确切的原因,为什么我们需要第一次调用send(None)
,因为生成器将返回i
(yield i
)之前将值分配给X
答案 2 :(得分:6)
def gen():
i = 1
while True:
i += 1
x = yield i
print(x)
m = gen()
next(m)
next(m)
m.send(4)
结果
None
4
查看上面更简化的代码。
我认为引起你困惑的是'x = yield i'的判断,
这个声明并没有说从send()方法接受的值被赋予我然后我命名为x。
相反,值i由yield statment返回到生成器,x由send()方法确定。一个语句同时做两件事。
答案 3 :(得分:0)
由于您甚至要求提出意见,请考虑以下情况:
def lambda_maker():
def generator():
value = None
while 1:
value = yield value
value= value[0][1]
f = generator()
next(f) # skip the first None
return f.send # a handy lambda value: value[0][1]
现在以下两行是等效的:
a_list.sort(key=lambda a: a[0][1])
a_list.sort(key=lambda_maker())
(顺便说一句,在当前(2018-05-26,GDPR之后的第1天)CPython2和CPython3实现中,第二行比第一行运行更快,但这是一个与每个函数调用的帧对象初始化开销。)
这里发生了什么? lambda_maker
调用f=generator()
并获取生成器;调用初始next(f)
开始运行生成器并使用初始None
值,并在yield
行暂停。然后它将绑定方法f.send
返回给它的调用者。从这一点开始,每次调用此绑定方法时,generator.value
local接收绑定方法的参数,重新计算value
,然后循环回yield
当前值{{} 1}}并等待下一个value
获得另一个值。
生成器对象保留在内存中,它在循环中所做的全部是:
.send
的参数)答案 4 :(得分:0)
注意:
为简单起见是我的回答限于当发电机在每行至多1 yield
的命令的情况。
<强> TL; DR:强>
.send()
方法:
发送值是“接收”的产量表达本身。
这意味着表达yield 7
7
,但(yield 7)
,可以是例如"hello"
。<强>前言强>
让g = gen()
是发电机迭代器的实例gen().
该命令next(g)
的行为的准确为g.send(None)
。
next()
功能。发送的不 - None
值仅允许如果实例g
是悬浮在与yield
命令的发言:
.send()
方法发送的值的直接将悬浮产量表达(参见<强> 4 强>在点在“一步一步”部分波纹管)。所以发送之前未 - None
的值,必须把发电机实例这样的暂停状态通过向它的 None
强>值。它可以是简单为g.send(None)
:
但是,仅仅前g
变得悬浮,它产生的yield
命令的值。这样得到的值成为.send()
方法的返回值:
我们可能想的在以后的使用一个变量使用这个接收值的或保存它,这样,而不是先前的两个画面,让我们开始我们的旅程,这样的:
<强>一步一步:强>
在第一.send()
启动实例g
。实例:g
开始执行它的命令到第一yield语句,这产生其值:
这意味着,即在可变from_iterator_1
将是串"first_from_iterator"
。
现在,得到它的第一个值之后,我们有g
在暂停状态
,其允许我们发送给g
的东西的有用下,除了None
- 例如数1
。
如g
悬浮在表达 yield "first_from_iterator"
,
的值这个表达式(本身)将成为1
。
(是的,yield "first_from_iterator"
的就是强>的表达,同样地a + b
是。)
回想一下,此时的值"first_from_iterator"
是很久以前已经产生。
在实例g
,则唤醒,以及 - 继而 - {的{1}}现在等待一个返回值。
在先前暂停,现在唤醒语句将被执行。
(该悬浮液之前,它未进行,它仅产生一个值。)
在我们的简单情况下(在沃肯语句是g.send()
)仍然没有进行,但什么
保存SENT值(yield "first_from_iterator"
)给一个变量为了在后面使用而不是
1
或与它进行更复杂的计算,而不是
received_1 = yield "first_from_iterator"
在所有随后的声明,result = math.cos((yield "first_from_iterator") * math.pi) # result: -1
,将执行,但只到与g
在它命令的下一个语句。
下一条语句(其中包含 yield
命令)产生一个值
地中断yield
再次并唤醒等待g
方法(通过提供其预期的 - 产生 - 返回值)。
它允许在它之后执行下一个命令:
现在我们的在相同的情况如在点2 强> - 所以在故事将被重复 - 刚刚执行(下一个).send()
方法之前强>
注意:
将重复的用相同的问题在上面的“前言”部分的最后一点 - 我们可能不想抛出了产生价值,所以不是命令
.send()
是更好地利用作为事
g.send(1) # Not very appropriate
(以及类似地在接下来的from_iterator_2 = g.send(1) # Saving the 2nd yielded value
命令)。
(第一g.send(2)
(与该send
参数)开始执行它的命令的生成器实例。)