深入理解Python中的yield关键字

一、yield的英文单词意思是生产,刚接触Python的时候感到非常困惑,一直没弄明白yield的用法。python2和python3是不兼容的。

二、简单的yield实例:

以前只是粗略的知道yield可以用来为一个函数返回值【塞数据】,比如下面的例子:

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

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

运行结果:

取出alist的每一项,然后把i + 1塞进去。然后通过调用取出每一项。

这的确是yield应用的一个例子,但是,看过很多东西,并自己反复体验后,对yield有了一个全新的理解,其中这篇算是精品了。

三、包含yield的函数:

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

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

h()

运行结果:

可以看到,调用h()之后,print 语句并没有执行!这就是yield。具体的内容后面会越来越清晰,包括yield的工作原理。

四、yield是一个表达式:

python 2.5以前,yield是一个语句,我也没有考证,因为早都不用了,现在yield是一个表达式:

m = yield 5

表达式(yield 5)的返回值将赋值给m,所以,m = 5 肯定是错的。

那么如何获取(yield 5)的返回值呢?需要用到【send(msg)】。

五、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
"""

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内部已经做得很好了,一般不太需要手动去做这件事情。

#############################################################################

鸣谢:特别感谢所有在CSDN等网站热爱技术、乐于分享的工程师们。

说明:本文只是个人学习之用。

#############################################################################

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值