免费下载软件的网站有哪些,如何创建微网站,什么网站需要数据库,站长 网站ip这一次#xff0c;田辛老师想通俗易懂地解释一下Python中的yield功能。
本文要说明以下四个问题#xff1a;
yield是什么什么是迭代器和生成器yield的基本用法如何使用yield from
用真正简单的方法讲解yield并不容易。 我想#xff0c;就算你不懂yield语句#xff0c;也…这一次田辛老师想通俗易懂地解释一下Python中的yield功能。
本文要说明以下四个问题
yield是什么什么是迭代器和生成器yield的基本用法如何使用yield from
用真正简单的方法讲解yield并不容易。 我想就算你不懂yield语句也能从我的文档中有所收获。 这篇文章为了让读者理解举了一个未必特别恰当的例子。 不过例子只是例子重要的是了解原理。
本文要求环境版本高于Python3.7以上。
1. yield是什么
yield是一种能够暂时中止函数执行的语句。 您可以用它返回此时的返回值并重新启动。 要了解yield 你必须了解迭代器和生成器。
ps. 这两句话目前不理解没关系 后面的例子我们会反复解读这两句话
2. 什么是迭代器和生成器
迭代器是一种可以迭代以检索元素的类型。 Python的下面list、dict、tuples都可以迭代的所以这些对象就是迭代器。
生成器生成器通过每次取出元素执行处理来生成元素。
这里田辛老师先大致说一句为什么yield这么重要迭代器在创建可迭代对象的时候 并不是一次性生成的。 比如我们现在需要一些员工编号的list如果不用yield通常的做法我们生成一个list内容是完整的员工编号1-100。 通常是一次性生成此时内存中就有一个包含着1-100这100个数字的列表。
通常情况下这没什么问题。 但是一旦我们列表中的内容非常占用内存。 在大数据场景下消耗资源特别大的时候这就会影响我们程序执行效率以及设备的负荷。 如果你使用了yield这就会好很多。 yield的机制并不会生成一个1-100的序列在那里占内存。 而是只有生成器在要求的时候这个值才会被生成而且用过即焚不用空占内存。
好了先了解到这里。如果你不理解我们一边用一边理解。
3. yield 的基本用法
3.1 编写迭代器
yield 基本上是函数内部使用的语句。 基本语法是: yield value 我们举一个员工的例子我们做一个基础编号池为了简单这里我给基础编号池3个编号。 这实际上就是迭代器。
def base_code_pool():BASE编号池 1-3 for i in range(3):yield BASE-%s % str(i 1)那么这个迭代器怎么使用呢 还记得田辛老师在最开始的时候说”yield是一种能够暂时中止函数执行的语句。您可以用它返回此时的返回值并重新启动。“
也就是我们必须用生成器生成迭代器才有意义。
3.2 编写生成器
那么我们来做写这个生成器
gen base_code_pool()就这么简单一行代码那么如何使用呢?
print(next(gen))
print(next(gen))
print(next(gen))有了迭代器有了生成器 那么我们3次请求这个编号池输出结果就是
BASE-1
BASE-2
BASE-3
[Finished in 167ms]可以看到每次执行都会产生一个新的编号。
但是这里要注意迭代器里面多少个元素就只能请求多少次。 多了会报错。
比如我们再多请求一次。 也就是在刚才的print 代码的部分写四个print(next(gen)) 你会发现前三个正确表示第四个会报错。田辛老师的执行结果
BASE-1
BASE-2
BASE-3
Traceback (most recent call last):File D:\develop\python\di08-tdd-cdg-python-learning\src\adv_yield\yield_test.py, line 12, in moduleprint(next(gen))
StopIteration
[Finished in 172ms]这一点要牢记。
这里要说一个小故事 事实上Python3及更早版本中存在一个next()函数 只不过已经消失。取而代之的是引入了__next__()方法。看到这个方法的名字我们就知道这是一个特殊方法。 特殊方法在一般程序中大量调用肯定不太好。 于是Python后来的版本又提供了一个next的内置函数。 现在的next()函数是调用的__next__()方法。 所以现在的next() 并非更早期的next()
为了证明这点我么再举一个例子还是刚才的BASE迭代器只不过在使用生成器的时候这么写
gen base_code_pool()print(gen.__next__()) # 证明了next()函数和__next__()方法的关系
print(list(gen)) # 证明BASE-1已经被释放掉了这样的执行结果是
BASE-1
[BASE-2, BASE-3]
[Finished in 157ms]这里我们首先证明了next()函数和__next__()方法的关系。 另外注意我们在生成了一个编号后列出了剩下的全部编号。 你会发现 BASE-1并不在这个list里面 为什么呢 因为用后即焚BASE-1已经被释放掉了。 这也就是我们一直强调yield 非常节省内存的原因
4. 关于yield from
现在我们已经学会了如何使用基本的yield让我们来谈谈yield from。
yield from主要用于将生成器拆分成更小的部分。这是从 Python 3.3 添加的语法所以它不能在早期版本中使用。
比如刚才的编号是你项目组组员的编号。 公司就给了你三个资源的名额并且说人不够就使用外包。 那么在这种情况下 你的生成器可以做一些调整来适应这个情况。
首先外包人员也需要一个迭代器
def outsource_pool():外包编号池OUTS 1-30for i in range(30): # 好吧给了你30个外包的名额yield OUTS-%s % (i 1)现在我们有了两个编号池 我们的原则是先用BASEBASE的编号用完了以后就用OUTS。 那么这个逻辑怎么优雅的实现呢 下面就用到了 yield from 参考下面代码
def team_member_code():team_member迭代器yield from base_code_pool()print(内部资源编号用完开始使用外包)yield from outsource_pool()我们写了一个team_member_code()迭代器 这个迭代器里面有两个小迭代器 base写在上面 外包写在下面。 OK 我们来调用一下看看执行结果
team_member team_member_code()
print(next(team_member))
print(next(team_member))
print(next(team_member))
print(next(team_member))
print(next(team_member))执行结果是
BASE-1
BASE-2
BASE-3
内部资源编号用完开始使用外包
OUTS-1
OUTS-2
[Finished in 138ms]那么这样我们就简单的实现了我们刚才组织资源用完了用外包的需求。
而且这里也证明了实际上迭代器的执行过程是在yield 的位置中断的。 所以“资源用完”的提示才会出现在子迭代器切换的位置。
我们来试试直接listprint(list(team_member_code())) 目的是一次性列出所有编号。
输出结果
内部资源编号用完开始使用外包
[BASE-1, BASE-2, BASE-3, OUTS-1, OUTS-2, OUTS-3, OUTS-4, OUTS-5, OUTS-6, OUTS-7, OUTS-8, OUTS-9, OUTS-10, OUTS-11, OUTS-12, OUTS-13, OUTS-14, OUTS-15, OUTS-16, OUTS-17, OUTS-18, OUTS-19, OUTS-20, OUTS-21, OUTS-22, OUTS-23, OUTS-24, OUTS-25, OUTS-26, OUTS-27, OUTS-28, OUTS-29, OUTS-30]
[Finished in 172ms]我们可以看到 如果直接list()“资源用完”这句话出现的位置也说明了他的执行过程。
***特别声明这个和实际场景有出入田辛老师作为做了很多年外包项目也管理了很多年带有外包的团队的IT老兵没有对外包的同学任何轻视。 这里只是个例子。 ***
5. 总结
本文记录的实际上是yield的最基础知识。 yield的基础知识点可以归纳为
迭代器和生成器用 yield 时重复处理使用的内存更少在 for 循环中处理或使用生成器的next()函数可以利用 yield from 将一个迭代器划分为多个小迭代器从而进行精细化处理。
6. 代码
老规矩所有的过程代码附上
#!/usr/bin/env python
# -*- coding:utf-8 -*-#-----------------------------------------------------------------------------
# --- TDOUYA STUDIOS ---
#-----------------------------------------------------------------------------
#
# Project : di08-tdd-cdg-python-learning
# File : yield_test.py
# Author : tianxin.xpgmail.com
# Date : 2023/2/12 21:57
#
# 用于整理yield使用教学的测试代码
#
#--------------------------------------------------------------------------def base_code_pool():BASE编号池 1-3 for i in range(3):yield BASE-%s % str(i 1)# gen base_code_pool()# print(next(gen))
# print(next(gen))
# print(next(gen))
# print(next(gen))# print(gen.__next__())
# print(list(gen))def outsource_pool():外包编号池OUTS 1-30for i in range(30): # 好吧给了你30个外包的名额yield OUTS-%s % (i 1)def team_member_code():team_member迭代器yield from base_code_pool()print(内部资源编号用完开始使用外包)yield from outsource_pool()# team_member team_member_code()# print(next(team_member))
# print(next(team_member))
# print(next(team_member))
# print(next(team_member))
# print(next(team_member))print(list(team_member_code()))