『无为则无心』Python基础|『无为则无心』Python基础 — 63、Python中的生成器
目录
- 1、为什么要有生成器
- 2、创建生成器
- (1)简单创建生成器
- (2)生成器的使用
- 3、
yield
关键词- (1)
yield
关键词说明 - (2)
send()
方法说明
- (1)
- 4、使用
yield
实现斐波那契数列 - 5、总结
1、为什么要有生成器 Python在数据科学领域可以说是很火,我想有一部分的功劳就是它的生成器了吧。
我们知道我们可以用列表储存数据,可是当我们的数据特别大的时候,列表中的数据都是放在内存中,受到内存限制,列表容量肯定是有限的,而且还会降低计算机的性能。
如果仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。但如果列表中元素是按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的列表数据,从而节省大量的空间。
换句话说,我又想要得到庞大的数据,又想让它占用空间少,这时生成器就派上用场了,它可以说是一个不怎么占计算机资源的一种方法。
2、创建生成器 (1)简单创建生成器
将一个列表推导式(也叫列表生成式)
[]
改为 ()
即可创建一个生成器。# 1.用推导式定义一个列表
# 关于推导式请看以前的文章有讲解。
my_list = [x * x for x in range(10)]# 打印列表
print(my_list)
# 查看my_list类型,是一个列表
print(type(my_list))
"""
输出结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
"""# 2.创建一个生成器
my_gen = (x * x for x in range(10))# 打印生成器,是一个生成器对象
print(my_gen)
# 查看my_gen对象类型,是生成器类型
print(type(my_gen))
"""
输出结果:
at 0x0000000002575148>
"""
(2)生成器的使用
# 创建生成器
my_gen = (x * x for x in range(10))# 1。方式一:遍历生成器,使用next方法
print(my_gen.__next__())# 0
print(my_gen.__next__())# 1
print(my_gen.__next__())# 4
print(my_gen.__next__())# 9
# 或者
print(next(my_gen))# 16
print(next(my_gen))# 没有数据了则会抛出异常StopIteration# 2.方式二:遍历生成器的内容
for i in my_gen:
print(1)# 3.方式三:遍历生成器的内容
while True:
try:
# 调用next函数,获取下一个字符
result = next(my_gen)
print(result)
except StopIteration:
# 释放对it的引用,即废弃迭代器对象
del my_gen
# 不推出循环会成为私循环
break
提示:
- 在上边练习中,可以看到和迭代器的用法差不多,在这里说明一下生成器本身就是一个迭代器。如果有对迭代器不清楚的可以查看前面说明迭代器的文章。
- 上面方式一不断调用
next()
方法回去元素,实在是太变态了,正确的方法是使用for
循环。generator
保存的是算法,每次调用next()
,就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
总结:3、
- 用
[]
推导出来的是迭代器(Iterables
)。- 用
()
推导出来的是生成器(Generators
)。
yield
关键词
(1)yield
关键词说明如果我们想定义一个自己的生成器函数怎么办?
Python有
yield
关键词。其作用和return
的功能差不多,就是返回一个值给调用者,只不过有yield
的函数返回值后,函数依然保持调用yield
时的状态,当下次调用的时候,在原先的基础上继续执行代码,直到遇到下一个yield
或者满足结束条件结束函数为止。啥意思?啥意思?啥意思?
- 你先把
yield
关键字直观的看做return
关键字,它首先是return
的功能,就是在函数或方法中返回某个值,返回之后程序就不再往下运行了。 yield
相当于返回一个值给调用者,停止执行函数中的语句,并且记住这个返回的位置。下次迭代时(或者执行next
方法的时候),代码从yield
记录位置的下一条语句开始执行。- 带有
yield
的函数不再是一个普通函数,而是一个生成器generator
。 - 调用一个生成器函数,返回的是一个迭代器对象。
# 定义一个生成器函数
def testYield():
yield 1
yield 2
yield 3# 获得一个生成器对象
ty = testYield()"""
调用过程:
next(ty)相当于ty.__next__()
掉调用一次next(ty)时
就会执行testYield()内的方法。
当执行的第一行, yield 1时,
返回当前yield的值1给调用者,停止向下执行,并记录函数中当前的执行位置。
也就是每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。
程序执行结束当下次再调用next(ty)的时候,
还是会执行testYield()内的方法,
只不过是从yield 1下面一句开始执行。
以此类推。
"""
print(next(ty))# 1
print(next(ty))# 2
print(next(ty))# 3
print(next(ty))# StopIteration
注意:每次调用(2)testYield()
函数都会生成一个新的 generator 实例,各实例互不影响。
send()
方法说明send()
方法和next()
方法一样,都能让生成器继续往下走一步(下次遇到yield停),但send()
能传一个值,这个值作为yield表达式整体的结果。def testYield():
yield 1
y = yield 2
if (y == 'hello'):
yield 9
yield 3ty = testYield()print(ty.__next__())# 1
print(next(ty))# 2
"""
第三次执行,send方法会把"hello"传递进去
就是y = "hello"
换句话说,就是send可以强行修改上一个yield表达式值
程序会从第二个yield的下一行开始执行
执行到下一个yield停止,并记录位置,返回结果。
"""
print(ty.send("hello"))# 9
print(next(ty))# 3
print(next(ty))# StopIteration
注意:第一次执行要么4、使用next(ty)
要么ty.send(None)
,不能使用ty.send('xxxxx')
,否则会报错的。
yield
实现斐波那契数列
"""
数学中有个著名的斐波那契数列(Fibonacci),
数列中第?个数0,第?个数1,其后的每?个数都可由前两个数相加得到:
如下:
0,1,1,2,3,5,8,13,21,34,...现在我们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。
那么这个斐波那契数列我们就可以?生成来实现,
每次迭代都通过数学计算来?成下?个数。
"""
from collections.abc import Iterable, Iteratorclass FibGenerator(object):
"""
fib数列生成器
"""# 初始化方法
def __init__(self):
# 斐波拉契数列中的前两个数
self.num1 = 0
self.num2 = 1# 用来记录迭代次数(计数器)
self.i = 0def gen(self, count):
# 用来保存迭代的总次数
self.count = count# 判断是否迭代结束,如果没有到达迭代次数,则返回数据
# self.count 需要迭代的次数
# self.i已迭代次数
while self.i < self.count:
yield self.num2
# 计算num1, num2的值,方便下次迭代返回
# 这里运用的是序列的封包与解包,不会的可以看我以前的文章(元组)
self.num1, self.num2 = self.num2, self.num1 + self.num2# 执行一次next方法,计数器+1
self.i = self.i + 1# 创建一个对象
fibGen = FibGenerator()# 调用生成器函数得到一个生成器
fg = fibGen.gen(15)# fibIter对象是一个迭代器
print(isinstance(fg, Iterable))# True
print(isinstance(fg, Iterator))# True# next方法方式获取数据
# print(next(fg))# 1
# print(next(fg))# 1
# print(next(fg))# 2
# print(next(fg))# 3
# print(next(fg))# 5
# print(next(fg))# 8# 遍历生成器,可执行
for li in fg:
print(li)
5、总结
- 生成器
generator
就是迭代器iterator
的一种,以更优雅的方式实现的iterator
,而且完全可以像使用iterator
一样使用generator
。 - 当然除了定义,定义一个
iterator
,你需要分别实现__iter__()
方法方法和__next__()
方法。但generator只需要一个yield关键字就可以。 - Python生成器主要目的就是为了让你的代码更省资源,更高效!
【『无为则无心』Python基础|『无为则无心』Python基础 — 63、Python中的生成器】参考:
- https://blog.csdn.net/weixin_37720172/article/details/78482291
- https://www.cnblogs.com/liangmingshen/p/9706181.html
- https://blog.csdn.net/xiangxianghehe/article/details/77281186
推荐阅读
- 面试官(Java 设计原则中,为什么反复强调组合要优先于继承())
- 详解混合云安全性与合规性,降低风险的指导原则
- 常见正则应用
- 实践GoF的23的设计模式(SOLID原则(下))
- MySQL为数据表建立索引的原则详解
- 前端必备知识|ES6学习笔记(4)——字符串\正则\数值\数组\函数\对象的扩展
- star法则java简历_在简历中使用STAR法则
- 设计原则之【迪米特法则】
- 轩辕展览-工业园区展厅设计的原则
- 『无为则无心』Python面向对象|『无为则无心』Python面向对象 — 59、魔法方法