深入理解python中的yield
yield的英文单词意味着生产。当我第一次接触Python时,我感到非常困惑。我从来没有理解yield的用法。
只是大致知道yield可以用来为函数返回值塞数据,例如以下例子:
defaddlist(alist): foriinalist: yieldi+1
取出alist的每一项,然后取出ilist + 塞进去。然后通过调用取出每一项:
alist=[1,2,3,4] forxinaddlist(alist): printx
这确实是yield应用的一个例子
1. 包含yield的函数
如果你看到一个函数包含yield,这意味着这个函数已经是一个generator,它的执行将与其他普通函数有很多不同。例如,以下简单函数:
defh(): print'Tobebrave' yield5 h()
可以看出,调用h()后,print 句子没有执行!这是yield,那么如何让printt呢? 语句执行情况如何?这就是后面要讨论的问题,通过后面的讨论和学习,你就会明白yield的工作原理。
2. yield是一种表达式
Python2.过去,yield是一个句子,但现在在2.5中,yield是一个表达式(Expression),比如:
m = yield 5
表达式(yield 5)返回值将赋值m,因此,认为 m = 5 这是错误的。那么如何获得呢?(yield 5)返回值怎么样?需要使用后面介绍的send(msg)方法。
3. 通过next()语句看原理
现在,让我们揭示yield的工作原理。众所周知,我们上面的h()调用后没有执行,因为它有yield表达式,所以我们让它通过next()语句执行。next()语句将恢复Generator执行,直到下一个yield表达式。例如:
defh(): print'WenChuan' yield5 print'Fighting!' c=h() c.next()
c.next()调用后,h()开始执行,直到遇到yield。 所以输出结果:
Wen Chuan
当我们再次调用c时.next()在找到下一个yield表达式之前,它将继续执行。因为后面没有yield,所以会抛出异常:
WenChuan Fighting! Traceback(mostrecentcalllast): File"/home/evergreen/Codes/yidld.py",line11,in<module> c.next() StopIteration
4. send(msg) 与 next()
在了解了next()如何执行包含yield的函数后,让我们来看看另一个非常重要的函数send(msg)。事实上,next()和send()在某种意义上是相似的,区别在于send()可以传递yield表达值,而next()不能传递特定值,只能传递none。因此,我们可以把它看作
c.next() 和 c.send(None) 功能是一样的。
让我们看看这个例子:
defh(): print'WenChuan', m=yield5#Fighting! printm d=yield12 print'Wearetogether!' c=h() c.next()#相当于c.send(None) c.send('Fighting!')#(yield5)表达式被赋予'Fighting!'
输出结果如下:
Wen Chuan Fighting!
需要提醒的是,第一次调用时,请使用next()语句或send(None),不要用send发送非none值,否则会出错,因为没有yield语句来接收值。
5. send(msg) 与 next()返回值
send(msg) 和 next()有返回值,它们的返回值非常特殊,返回的是下一个yield表达式的参数。例如yield 5,则返回 5 。在这里,你明白了什么吗?在本文的第一个例子中,通过for i in alist 遍历 Generator,事实上,alist每次都被调用.Next(),每次alist,.Next()返回值是yield的参数,也就是我们开始认为被压进去的东东。让我们继续上面的例子:
defh(): print'WenChuan', m=yield5#Fighting! printm d=yield12 print'Wearetogether!' c=h() m=c.next()#m获得yield5的参数值5 d=c.send('Fighting!')#得到yield12的参数值12 print'Wewillneverforgetthedate',m,'.',d
输出结果:
WenChuanFighting! Wewillneverforgethedate5.12
6. throw() 与 close()中断 Generator
中断generator是一项非常灵活的技能,可以通过throw抛出generatorexit异常来终止generator。Close()方法的功能是一样的。事实上,它在内部调用了throw(GeneratorExit)是的。我们看:
defclose(self): try: self.throw(GeneratorExit) except(GeneratorExit,StopIteration): pass else: raiseRuntimeError("generatorignoredGeneratorExit") #Otherexceptionsarenotcaught
因此,当我们调用close()方法时,我们可以调用next()或send(msg)如果出现异常,就会抛出异常:
Traceback(mostrecentcalllast): File"/home/evergreen/Codes/yidld.py",line14,in<module> d=c.send('Fighting!')#得到yield12的参数值12 StopIteration