1. 生成器初始
什么是生成器?这个概念比较模糊,各种文献都有不同的理解,但是核心基本相同。生成器的本质就是迭代器,在python社区中,大多数时候都把迭代器和生成器是做同一个概念。不是相同么?为什么还要创建生成器?生成器和迭代器也有不同,唯一的不同就是:迭代器都是Python给你提供的已经写好的工具或者通过数据转化得来的,(比如文件句柄,iter([1,2,3])。生成器是需要我们自己用python代码构建的工具。最大的区别也就如此了
1.1 生成器的构建
在python中有三种方式来创建生成器:
-
- 通过生成器函数
-
- 通过生成器推导式
- 3. python内置函数或者模块提供(其实1,3两种本质上差不多,都是通过函数的形式生成,只不过1是自己写的生成器函数,3是python提供的生成器函数而已)
1.2 生成器函数
我们先看一个很简单的函数:
def func():
print('in func1')
return 22
func()
print(func())
>>>
in func1
in func1
22
将函数中的return换成yield,这样func就不是函数了,而是一个生成器函数
def func():
print('in func1')
yield 22 #函数中存在yield,那么这个函数就是一个生成器函数
func()
print(func())
>>>
<generator object func at 0x0000018C4DED2E48>
运行的结果和最上面的不一样,为什么呢?? 由于函数中存在yield,那么这个函数就是一个生成器函数.
1.2.1 生成器的取值
生成器的本质就是迭代器.迭代器如何取值,生成器就如何取值。所以我们可以直接执行next()来执行以下生成器
def func():
print('in func1')
yield 22
print(func())
print(next(func()))
>>>
<generator object func at 0x000002CF72A02EC8>
in func1
22
1.2.2 return and yield 区别
- return 函数中只存在一个return结束函数,并且给函数的执行者返回值。
- yield 只要在函数中有yield那么它就是生成器函数而不是函数了。
生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next
举列
我们来看一下这个需求:老男孩向楼下卖包子的老板订购了10000个包子.包子铺老板非常实在,一下就全部都做出来了
def eat():
lst = []
for i in range(1,10000):
lst.append('包子'+str(i))
return lst
print(eat())
这种方式是可以,但是是个消耗内存的方法;但是我们由于学生没有那么多,只吃了2000个左右,剩下的8000个,就只能占着一定的空间,放在一边了。如果包子铺老板效率够高,我吃一个包子,你做一个包子,那么这就不会占用太多空间存储了,完美..
def eat():
for i in range(1,10):
yield '包子'+str(i)
e = eat()
for i in range(5):
print(next(e))
# 多次next包子的号码是按照顺序记录的。
>>>
包子1
包子2
包子3
包子4
包子5
这两者的区别
- 第一种是直接把包子全部做出来,占用内存。
- 第二种是吃一个生产一个,非常的节省内存,而且还可以保留上次的位置。
1.2.3 yield from
提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
# 对比yield 与 yield from
def func():
lst = ['卫龙','老冰棍','北冰洋','牛羊配']
yield lst
g = func()
print(g)
print(next(g)) # 只是返回一个列表
def func():
lst = ['卫龙','老冰棍','北冰洋','牛羊配']
yield from lst
g = func()
print(g)
# 他会将这个可迭代对象(列表)的每个元素当成迭代器的每个结果进行返回。
print(next(g))
print(next(g))
print(next(g))
print(next(g))
'''
yield from ['卫龙','老冰棍','北冰洋','牛羊配']
等同于:
yield '卫龙'
yield '老冰棍'
yield '北冰洋'
yield '牛羊配'
'''