「python」yield

第一次看到yield,懵逼,百度谷歌后,懵圈,直到看到了这篇文章 通俗易懂,部分内容其实有待考究,但好歹让人知道yield是什么了,把复杂的东西变简单这就是功力啊。
都说某个函数包含了yield,这意味着这个函数已经是一个生成器(generator)
那什么是生成器(generator)?

创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了,浪费可耻啊。
so,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

简单说:python生成器是迭代器的一种,特殊之处在于一旦遇到yield都会被挂起且返回yield右边的值,之后调用next()函数和send()函数可唤醒下一次迭代,起点是 : 上次迭代遇到yield时后面的代码(下一行),开始执行

理论结合实践,看下面代码吧:

# _*_ coding: utf-8 _*_
__author__ = 'ing'
__date__ = '2019-07-18 16:28'


def foo():
    print('starting...')
    while True:
        print("rolling...")
        p = yield 5
        print('p ---> ', p)


g = foo()
print("---------1---------")
print(next(g))
print("---------2---------")
print(next(g))
print("---------3---------")


暂停2分钟🤔下 输出结果
.
.
.
一休哥,回来了……

执行结果

---------1---------
starting...
rolling...
5
---------2---------
p --->  None
rolling...
5
---------3---------

来来,让我们单步调试:
1.程序开始执行后 g = foo(),此时日志只打印了

---------1---------

你应该会问不应该是先

starting...

么?
这说明foo函数并没有真的执行,由于foo函数中yield关键字,而是先得到一个生成器g
2.直到调用next方法,foo函数正式开始执行,先执行print 也就输出了starting...,然后进入while循环开始,打印了rolling...,这时候,关键的时刻到了,遇到了yield,此刻你把它看成‘return’,返回一个5之后,程序中断执行,并没有执行赋值给p的操作。到此,第一个next(g)执行结束:

starting...
rolling...
5

3.程序执行print("---------2---------")
4.再次执行print(next(g)) ,这个时候和上面不同的是这个时候是从上次next程序中断的地方开始执行的,也就是要执行p的赋值操作,要注意,这时的赋值操作右边是没有值的(因为‘return’出去了,并没有赋值操作给左边的传参数 #1),所以这个时候p赋值是None,所以日志显示

---------2---------
p --->  None

接着往下执行,进入到循环里面,执行
print("rolling...")
执行
p = yield 5
和上面的一样,return 5 然后中断,p 未被赋值,为None.

rolling...
5

最后执行print("---------3---------")结束

到这你该明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next挂起的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步挂起的地方开始,然后遇到yield后,return出要生成的数,结束。


上面也算理解了yield,另外认识了next的作用,下面我们再认识下send,和next有什么区别。
修改下上面的程序,把第二个next替换成send方法,如下:

# _*_ coding: utf-8 _*_
__author__ = 'ing'
__date__ = '2019-07-18 16:28'

def foo():
    print('开始--->')
    while True:
        print("循环--->")
        p = yield 5
        print('p ---> ', p)

g = foo()
print("---------1---------")
print(next(g))
print("---------2---------")
print(g.send(7))
print("---------3---------")

先执行看下效果:

---------1---------
开始--->
循环--->
5
---------2---------
p --->  7
循环--->
5
---------3---------

对比下可以看出区别在于next()之后 ,p不再是None而是被赋值了。这是因为,send是发送一个参数给p的,因为上面讲到,'return'的时候,并没有把5赋值给p,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次return 5后执行,先把7赋值给了p,然后执行next的作用,遇到下一回的yield,return出结果后结束。
4.程序执行g.send(7),从yield关键字的下一行开始继续执行,send会把7这个值赋值给p变量
5.由于send方法包含next()方法,所以程序会继续往下执行,然后再次进入while循环
6.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次挂起,直到再次调用next方法或send方法。
,,,,
至此,想必应该大致理解了吧,不理解?再看一遍。。。
最后看一段代码

def foo(num):
    print("starting...")
    while num<10:
        num=num+1
        yield num
for n in foo(0):
    print(n)

运行结果是什么呢?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1.1==,is的使用 ·is是比较两个引用是否指向了同一个对象(引用比较)。 ·==是比较两个对象是否相等。 1...
    TENG书阅读 792评论 0 0
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 3,233评论 0 9
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 14,259评论 0 38
  • Python语言特性 1 Python的函数参数传递 看两个如下例子,分析运行结果: 代码一: a = 1 def...
    伊森H阅读 3,178评论 0 15
  • 可根据孩子的兴趣做深度延展 我家儿子这几天迷上轮船,把所有的一切当作轮船,如盖被子,他想象成坐轮船。而且上周他提出...
    喜乐妈妈阅读 231评论 0 1

友情链接更多精彩内容