生成器
生成器对象
概念
1.本质
还是内置有__iter__和__next__的迭代器对象
2.区别
迭代器对象是解释器自动提供的
数据类型\文件对象>>>:迭代器对象
生成器对象是程序员编写出来的
代码、关键字>>>:迭代器对象(生成器)
3.生成器的作用:
节省内存空间
4.创建生成器的基本语法
定义函数的时候,函数内部有yield关键字,就是生成器
函数调用之前就是普通函数,函数调用之后就变成了生成器(迭代器)
5.yeild的功能:
停顿代码
返回数据值
接收外部数据
def index():
print('from index')
print('hello world')
yield
'''
1.函数体代码中如果有yield关键字
那么函数名加括号并不会执行函数体代码
会生成一个生成器对象(迭代器对象)
'''
res = index()
'''2.使用加括号之后的结果调用__next__才会执行函数体代码'''
res.__next__()
# 输出结果是:
# from index
# hello world
'''3.每次执行完__next__代码都会停在yield位置 下次基于该位置继续往下找第二个yield'''
生成器一定是迭代器
迭代器不一定是生成器
def index():
print('from index')
print('hello world')
yield 123
print('second')
yield 222
print('three')
yield 333
print('four')
res = index() # 此时就是生成器
r1 = res.__next__() # 代码遇到yield关键字,会停住,夯住
>>> from index
>>> hello world
print(r1)
>>> 123
'''4.yield还有点类似于return 可以返回yield后面的值'''
r2 = res.__next__()
>>> second
print(r2)
>>> 222
r3 = res.__next__()
>>> three
print(r3)
>>> 333
r4 = res.__next__()
>>> four
>>> StopIteration # 报错,没有找见yield
生成器的案例
实现range方法:
'''主要是学习思想'''
自定义生成器对标range功能(一个参数 两个参数 三个参数 迭代器对象)
for i in range(1, 10):
print(i)
1.先写两个参数的
2.再写一个参数的
3.最后写三个参数
# 1.两个参数
def my_range(start, stop):
while start < stop:
yield start
start += 1
# 调用1:
# 用for循环调用
for i in my_range(1, 10): # my_range(1, 10)是迭代器对象,也是可迭代对象
print(i)
# 调用2:
# 每次单独调用
res = my_range(1, 10)
print(res.__next__()) # 写10次
# 调用3:
# 用for循环内部原理调用
res = my_range(1, 10)
while True:
try:
print(res.__next__())
except StopIteration:
break
# 2.三个参数
def my_range(start, stop, step=1):
while start < stop:
yield start
start += step
# 调用
for i in my_range(1, 10, 2):
print(i)
# 3.一个参数
def my_range(start, stop=None, step=1):
# 判断stop是否有值,没有值说明用户只是给了一个值,起始数字应该是0,终止位置应该是传入的值
if not stop: # None是假
stop = start
start = 0
while start < stop:
yield start
start += step
# 调用
for i in my_range(3):
print(i)
# for i in my_range(1, 6):
# print(i)
# for i in my_range(1, 7, 2):
# print(i)
for i in my_range(10, 5):
print(i)
# 4.也可以写成:
def my_range(start, stop=None, step=1):
if start < stop:
if not stop:
stop = start
start=0
while start < stop: # 0<stop
yield start
start += step
else:
return '好好传值,别乱来'
# for i in my_range(1, 10):
for i in my_range(50, 10):
print(i)
yield传值(了解)
3.1 yield传值
def eat():
print('开始干饭')
while True:
food = yield
print('开始吃%s' % food)
res = eat() # 只是把函数变成了生成器
r1 = res.__next__() # 取值,取到第一次遇到yield,然后停住
print(r1)
>>> 开始干饭
>>> None
传值用send()
res = eat()
r1 = res.__next__()
# send()的作用:1. 调用了__next__ 2. 传参数给yeild
res.send('臭豆腐')
res.send('面包')
res.send('米饭')
res.send('面条')
res.send('包子')
>>>
>>> 开始干饭
>>> 开始吃臭豆腐
>>> 开始吃面包
>>> 开始吃米饭
>>> 开始吃面条
>>> 开始吃包子
3.2 总结传值方式
'''
不同的取值方式:
(学习了3种)
1.索引取值
2.迭代取值
3.yield传值(了解)
'''
1.索引取值
c = 'hello'
print(c[1]) # h
2.迭代取值
res = c.__iter__()
print(res.__next__()) # h
print(res.__next__()) # o
3.yield传值
3.3 迭代取值与索引取值的对比
索引取值
- 可以任意位置任意次数取值
- 不支持无序类型的数据取值:字典,集合不能索引取值
迭代取值
- 只能从前往后挨个取值,不能倒回去,除非从新生成一个新的迭代器
- 支持所有类型的数据取值(无序有序)
ps:两者的使用需要结合实际应用场景
yield与return的对比
yield
- 可以有返回值
- 函数遇到yield不会结束,只会'停住'
- yield关键字把函数变成了生成器,支持迭代取值了
return
- 可以有返回值
- 遇到return关键字直接结束函数运行
生成器表达式
先复习列表生成式:
l = [1, 2, 3, 4, 5, 6]
res = [i + 1 for i in l]
print(res)
>>> [2, 3, 4, 5, 6, 7]
生成器表达式说白了就是生成器的简化写法
作用:1.简化代码,2.节省内存
语法格式:
'''
语法格式:
res = (想每个元素进行的操作 for 变量 in 可迭代对象)
操作:
1.加字符串
2.算术运算
3.函数
'''
l = [1, 2, 3, 4, 5, 6]
res = (i + 1 for i in l) # 生成器
print(res)
>>> <generator object <genexpr> at 0x000001A56CD14F68>
# 调用1:
print(tuple(res))
>>> (2, 3, 4, 5, 6, 7)
# 调用2:
print(res.__next__()) # print(next(res))
print(res.__next__()) # print(next(res))
print(res.__next__()) # print(next(res))
print(res.__next__()) # print(next(res))
print(res.__next__()) # print(next(res))
print(res.__next__()) # print(next(res))
'''
注意:
生成器如果不执行__next__,内部的代码不会执行
'''
笔试题(有难度)
def add(n, i): # 普通函数 返回两个数的和 求和函数
return n + i
def test(): # 有yield,是生成器1
for i in range(4):
yield i
g = test() # 激活生成器1 # [0, 1, 2, 3]
print(g)
for n in [1, 10]:
g = (add(n, i) for i in g) # 新生成器2
"""
第一次循环:n=1
g = (add(n, i) for i in g) # 重新将一个生成器赋值给g
但是没有调用,所以没有执行
如果循环只有一次,直接赋值,
list(g)的输出结果是:[1,2,3,4]
第二次循环:
g = (add(n, i) for i in (add(n, i) for i in g))
'''
可以将上述语句写成:
g1 = test()
g2 = (add(n, i) for i1 in g1)
g = (add(n, i) for i2 in g2)
'''
但是没有调用,所以没有执行
如果循环有两次,赋值n=10, i1=[0,1,2,3],i2=[10,11,12,13]
list(g)的输出结果是:[20,21,22,23]
第三次循环
g = (add(n, i) for i in (add(n, i) for i in (add(n, i) for i in g)))
'''
可以将上述语句写成:
g1 = test()
g2 = (add(n, i) for i1 in g1)
g3 = (add(n, i) for i2 in g2)
g = (add(n, i) for i3 in g3)
'''
但是没有调用,所以没有执行
如果循环有三次,赋值n=20, i1=[0,1,2,3],i2=[20,21,22,23],i3=[40,41,42,43]
list(g)的输出结果是:[60,61,62,63]
"""
'''循环结束之后,才有调用语句,才开始赋值'''
res = list(g) # 激活新生成器2
print(res)
>>> [20, 21, 22, 23]
res = next(g)
print(res)
res = next(g)
print(res)
res = next(g)
print(res)
res = next(g)
print(res)
>>> 20
>>> 21
>>> 22
>>> 23
# A. res=[10,11,12,13]
# B. res=[11,12,13,14]
# C. res=[20,21,22,23]
# D. res=[21,22,23,24]
'''不用深入研究 大致知道起始数即可'''