首先, 拿好宝剑: 先把 yield 看做 "return",
普通的 return 是什么意思, 就是在程序中返回某个值, 返回之后程序就不再往下运行了.
看做 return 之后再把它看做一个是生成器 (generator) 的一部分(带 yield 的函数才是真正的迭代器),
返回的不是一个函数的输出, 是一个生成器的结果, 这样直到这个生成器全部执行完
好了, 带着宝剑继续看:
- def foo():
- print("starting...")
- while True:
- res = yield 4
- print("res:",res)
- g = foo()
- print(next(g))
- print("-" * 20)
- print(next(g))
代码输出:
- starting...
- 4
- --------------------
- res: None
- 4
按顺序逐句解释, 你绝对就清楚明白, 独步天下了:
1. 程序开始执行以后, 因为 foo 函数中有 yield 关键字, 所以 foo 函数并不会真的执行, 而是先得到一个生成器 g(相当于一个对象) .
===》获得技能: yield 和函数区别? 怎么使用?(有 yield 的函数就不是函数了, 是生成器, 不会随意执行, 想执行, 要么您调用 next 要么调用 send 方法, 要么您遍历)
2. 直到调用 next 方法, foo 函数才正式开始执行, 先执行 foo 函数中的 print 方法, 然后进入 while 循环.
3. 程序遇到 yield 关键字, 然后把 yield 想想成 return,return 了一个 4 之后, 程序停止, 并没有执行赋值给 res 操作, 此时 next(g)语句执行完成,
所以输出的前两行 (第一个是 while 上面的 print 的结果, 第二个是 return 出的结果) 是执行 print(next(g))的结果.
4. 程序执行 print("-" * 20), 输出 20 个 - .
5. 又开始执行下面的 print(next(g)), 这个时候和上面那个差不多, 不过不同的是, 这个时候是从刚才那个 next 程序停止的地方开始执行的,
也就是要执行 res 的赋值操作, 这时候要注意, 这个时候赋值操作的右边是没有值的(因为刚才那个是 return 出去了, 并没有给赋值操作的左边传参数),
所以这个时候 res 赋值是 None, 所以接着下面的输出就是 res:None.
===》获得技能: 生成器下一次调用 next/send 方法时, 从哪继续执行? (从刚才程序停止的下一步开始执行的, 这里下一步是该赋值了)
6. 程序会继续在 while 里执行, 又一次碰到 yield, 这个时候同样 return 出 4, 然后程序停止, print 函数输出的 4 就是这次 return 出的 4.
小结:
到这里你可能就明白 yield 和 return 的关系和区别了, 带 yield 的函数是一个生成器, 而不是一个函数了, 这个生成器有一个函数就是 next 函数, next 就相当于 "下一步" 生成哪个数,
这一次的 next 开始的地方是接着上一次的 next 停止的地方执行的, 所以调用 next 的时候, 生成器并不会从 foo 函数的开始执行, 只是接着上一步停止的地方开始, 然后遇到 yield 后, 输出要生成的数, 此步就结束.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
再来看个 send 的例子, 此例与前例区别: 只换了最后一行
- def foo():
- print("starting...")
- while True:
- res = yield 4
- print("res:",res)
- g = foo()
- print(next(g))
- print("*"*20)
- print(g.send(7))
输出结果:
- starting...
- 4
- ********************
- res: 7
- 4
先大致说一下 send 函数的概念: 此时你应该注意到上面那个的紫色的字, 还有上面那个 res 的值为什么是 None, 这个变成了 7, 到底为什么?
这是因为, send 是发送一个参数给 res 的, 因为上面讲到, return 的时候, 并没有把 4 赋值给 res, 下次执行的时候只好继续执行赋值操作, 只好赋值为 None 了, 而如果用 send 的话, 开始执行的时候, 先接着上一次 (return 4 之后) 执行, 先把 7 赋值给了 res, 然后执行 next 的作用, 遇见下一回的 yield,return 出结果后结束.
5. 程序执行 g.send(7), 程序会从 yield 关键字那一行继续向下运行, send 会把 7 这个值赋值给 res 变量
6. 由于 send 方法中包含 next()方法, 所以程序会继续向下运行执行 print 方法, 然后再次进入 while 循环
7. 程序执行再次遇到 yield 关键字, yield 会返回后面的值后, 程序再次暂停, 直到再次调用 next 方法或 send 方法.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
那么问题来了, 为什么要用生成器?
生成器相比一次列出所有的内容的有优势:
更节省存储空间
响应更迅速
使用更灵活
不废话, 上例子, 直观感受:
比如说我要 100 个数字 0,1,2,3,4,5,6............1000
你可能会这样写:
- for i in range(1000):
- print(i)
range(1000)就默认生成一个含有 1000 个数的 list 了, 所以很占内存.
我们可以用刚才的 yield 组合成生成器进行实现:
- def foo(n):
- print('---start---')
- while n <= 1000:
- yield n
- n += 1
- for n in foo(0):
- print(n)
好了, 就到这了, 希望诸位剑术有所变强, 江湖再见.
来源: https://www.cnblogs.com/liangmingshen/p/10970534.html