yield 关键字详解

当你看到一个函数里有yield声明时,用一个简单的小伎俩来理解发生了什么:

  1. 在函数体的开始插入一行; result = []
  2. 替换yield expr 为 result.append(expr)
  3. 在函数末尾添加一行:return result
  4. 现在就没有yield声明了,阅读并弄明白代码的含义吧!

这个小方法让你能够很好地理解函数整体做了什么,但是实际上yield的运行机制是和基于list的方法完全不一样的。在很多case下,yield的方法节省内存,而且速度更快。

理解 iterators, generators

首先,迭代器协议:

for x in mylist:

    ...loop body...

Python做了下面两个步骤:

  1. 获取一个针对mylist的迭代器:

           调用iter(mylist) -> 这个会返回一个拥有next()方法(或者__next__())

           这个步骤很多人会忘记告诉你。

     2. 使用迭代器来循环list中的元素:

        持续调用迭代器的next()方法,next()的返回值被赋给x,然后执行循环体的代码,如果异常StopIteration从next()中抛出,则意味着迭代器中没有数据了,循环就结束。

 

事实上,python在想要对任一个对象的内容进行循环迭代时,都会执行上述的两个步骤,这种方式可以是一个for循环,也可以像 otherlist.extend(mylist)。

在这里,mylist是可迭代的,因为它实现了迭代协议。在一个用户定义的类中,你可以实现__iter__()方法来让你的类具有可迭代性,这个方法会返回一个迭代器,迭代器有next()方法。在同一个类中实现__iter__()和next()是可行的。

 

注意,一个for循环是不知道它要处理的对象类型的,它只是遵循迭代器协议,通过调用next()一个个地迭代元素。内建的list一个个返回它的元素,字典一个个地返回它的key,文件一行行地返回,等等。

 

现在, 看看yield

def f123():

    yield 1

    yield 2

    yield 3

for item in f123():

    print item

如果不使用yield,函数f123()中,你将会有3个return声明,函数执行时,也只可能返回第一个return。但是,这里的f123已经不是一个普通的函数,当它被调用时,它不会返回任何yield声明里的值,而是返回一个生成器对象(generator object)。同时,函数并没有真正地退出,它进入一个挂起状态,当for循环尝试对这个生成器对象进行循环时,这个函数会从挂起状态继续运行,运行点是它上次返回的yield声明的下一行(或者下一个逻辑),从这里开始运行,遇到yield后再次返回声明的结果。直到生成器抛出StopIteration异常,循环结束。

 

因此,生成器对象就像一个适配器——在一端,它展示迭代器协议,通过__iter()__, next()使得for循环能够快乐地工作,另一端,它则要运行函数获得下一个值,并把函数再变为挂起状态

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值