深刻理解 Python yield

https://blog.csdn.net/lftaoyuan/article/details/78915518python

 

python2和python3是不兼容的,通篇环境都是python3.6git

简单的yield实例

之前只是粗略的知道yield能够用来为一个函数返回值塞数据,好比下面的例子:github

def addlist(alist): for i in alist: yield i + 1

 

取出alist的每一项,而后把i + 1塞进去。而后经过调用取出每一项:ruby

alist = [1, 2, 3, 4] for x in addlist(alist): print(x)

这的确是yield应用的一个例子,可是,看过不少东西,并本身反复体验后,对yield有了一个全新的理解,其中这篇算是精品了。ide

包含yield的函数

假如你看到某个函数包含了yield,这意味着这个函数已是一个Generator,它的执行会和其余普通的函数有不少不一样。好比下面的简单的函数:函数

def h(): print('study yield') yield 5 print('go on!') h()

 

能够看到,调用h()以后,print 语句并无执行!这就是yield。具体的内容后面会愈来愈清晰,包括yield的工做原理。lua

yield是一个表达式

python 2.5之前,yield是一个语句,我也没有考证,由于早都不用了,如今yield是一个表达式:idea

m = yield 5

表达式(yield 5)的返回值将赋值给m,因此,m = 5 确定是错的。spa

那么如何获取(yield 5)的返回值呢?须要用到send(msg).net

yield工做原理

揭晓yield的工做原理,须要配合next()函数。上面的h()被调用后并无执行,由于它有yield表达式,经过next()能够恢复Generator执行,直到下一个yield

def h(): print('study yield') yield 5 print('go on!') c = h() d1 = next(c) # study yield d2 = next(c) """ study yield go on! Traceback (most recent call last): File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module> d2 = next(c) StopIteration """
  • 1
  • 2
  • 3
  • 4

next()被调用后,h()开始执行,直到遇到yield 5

所以输出结果是:study yield

当咱们再次调用next()时,会继续执行,直到找到下一个yield。因为后面没有yield了,所以会拋出异常:

study yield go on! Traceback (most recent call last): File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module> d2 = next(c) StopIteration

 

send(msg) 与 next()

了解了next()如何让包含yield的函数执行后,咱们再来看另一个很是重要的函数send(msg)

其实next()send()在必定意义上做用是类似的

区别

send()能够传递yield的值

next()只能传递None

因此next() 和 send(None)做用是同样的。

def s(): print('study yield') m = yield 5 print(m) d = yield 16 print('go on!') c = s() s_d = next(c) # 至关于send(None) c.send('Fighting!') # (yield 5)表达式被赋予了'Fighting!'

 

输出的结果为:

study yield Fighting!

 

注意 生成器刚启动时(第一次调用),请使用next()语句或是send(None),不能直接发送一个非None的值,不然会报TypeError,由于没有yield语句来接收这个值。

send(msg) 与 next()的返回值

send(msg) 和 next() 的返回值比较特殊,是下一个yield表达式的参数(yield 5,则返回 5)。

到这里,第一个例子中,经过for i in alist 遍历 Generator,实际上是每次都调用了next(),而每次next()的返回值正是yield的参数:

def s(): print('study yield') m = yield 5 print(m) d = yield 16 print('go on!') c = s() s_d1 = next(c) # 至关于send(None) s_d2 = c.send('Fighting!') # (yield 5)表达式被赋予了'Fighting!' print('My Birth Day:', s_d1, '.', s_d2)

 

输出结果:

study yield Fighting! My Birth Day: 5 . 16

 

中断Generator

上面的例子中,当没有可执行程序的时候,会抛出一个StopIteration, 开发过程当中,中断Generator是一个很是灵活的技巧

throw

经过抛出一个GeneratorExit异常来终止Generator。

close

close的做用和throw是同样的,看它的源码,能够发现,它和raise一球样

def throw(self, type, value=None, traceback=None): '''Used to raise an exception inside the generator.''' # 用于在生成器中抛出一个异常。 pass def close(self): '''Raises new GeneratorExit exception inside the generator to terminate the iteration.''' # 在生成器中生成新的GeneratorExit异常来终止迭代。 pass

 

其实最后一个中断生成器能够忽略的,在开发过程当中,不可避免的要用到这些,可是Python3内部已经作得很好了,通常不太须要手动去作这件事情。

demo地址

https://github.com/seeways/PythonDemo/blob/master/static/yield_demo.py

相关文章
相关标签/搜索