python -生成器
1、生成器对象
- 定义:生成器其实是特殊的迭代器,可以称为自定义的迭代器,定义阶段为一个普通的函数,只有在调用的时候才将函数变成了生成器,第一次调用函数的时候并不会执行函数体代码,只是转换。
- 关键字:yield
- 函数中只要有yield关键字就为生成器!
实例如下:
# def my_range(start,stop = None,step=1):
# if stop ==None:
# stop,start = start,0
# # start = 0
# while start<stop:
# yield start
# start+=step
# # 函数名加括号转换成迭代器
# for i in my_range(0,10,2):
# print(i)
# for i in range(1,10):
# print(i)
# 生成器-yield
# 定义阶段为普通函数
def build():
print('第一次调用')
yield '在yield后面也可以写返回值哦!'
print('第二次调用')
yield '在yield后面也可以写返回值哦!'
# 调用函数不会执行代码,只是转变为生成器
build()
# 通过遍历res接收
res = build()
# 通过next()函数输出,输出注意调用next()函数和代码行数匹配
ret = res.__next__()
# 只有一个打印语句,在调用一次next()报错
# ret = res.__next__()
# 打印ret就是获取yield的返回值
print(ret)
'''每一次调用next()函数,代码会自动停在yield,并返回后面的值,再次调用会接着上次执行'''
res.__next__()
练习题:自定义range功能
实现1:1还原range()函数功能,用生成器对象
首先回忆一下range函数结构,range(start,stop,step)
实例如下:
# for循环遍历实现1-10
for i in range(1,10):
print(i)
# 自定义range()函数
# 按照range写参数
def my_range(start,stop = None,step=1):
# 如果只输入了一个值,末尾值为起始值互换
if stop == None:
stop,start = start,0
# 遍历输出
while start<stop:
yield start
# 实现步长功能
start+=step
# 函数名加括号转换成迭代器
for i in my_range(0,10,2):
print(i)
'''结果是一样的!'''
yield传值
用到send()函数,可以避免在yield后多次写入
def doing(name):
print(f'{name}走起来!')
while True:
some_wh = yield
print(f'{name}去{some_wh}')
# 第一次调用转换成生成器,不执行代码
res = doing('HammerZe')
res.__next__()
# 用send()传值
res.send('北京')
res.send('上海')
res.send('广州')
# 结果
# HammerZe走起来!
# HammerZe去北京
# HammerZe去上海
# HammerZe去广州
经过上面几个例子会发现,yield的作用和return有很大相似的地方,yield与return的异同如下:
-
相同点:
- 都可以返回一个或多个值(多个值的时候组织成元组返回)
-
不同点:
- 函数遇到yield不会结束而是“停住”,下一次调用会接着执行,return直接结束函数!
- yield可以将函数变成生成器,支持传值,用send()函数
2、生成器表达式
如何创建生成器?
- 列表生成式前面的文章写到过,这里的生成器只需将列表生成式的中括号改成小括号。
如何获取生成器的元素?有两种方法
-
for循环 / list遍历
-
next()函数依次遍历
实例如下:
# 创建一个列表生成式
l = [i for i in range(10)]
print(l)
# 转换为生成器
l1 = (i for i in range(10))
# 生成器只有通过遍历才会执行,下面为遍历的三种方法
for j in l1: # 或者list(l1)
print(j)
l2 = (i for i in range(10))
print(l2.__next__())
print(l2.__next__())
print(l2.__next__())
# 结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0
1
2
3
4
5
6
7
8
9
0
1
2
生成器对象和迭代器对象都是使用一次next()函数,才会打印出一句,而生成器的好处是可以一边计算和一边循环,生成器的使用节省了空间!
练习题:输出res的结果(有坑)
实例如下:
# 求和
def add(n, i):
return n + i
# 调用之前是函数 调用之后是生成器
def test():
for i in range(4):
yield i
g = test() # 初始化生成器对象
for n in [1, 10]: # 坑!!!
g = (add(n, i) for i in g)
"""
第一次for循环
g = (add(n, i) for i in g)
第二次for循环
g = (add(10, i) for i in (add(10, i) for i in g))
"""
# 这里的g取到的是最后一次遍历得到的n传进去计算的,n = 10
res = list(g)
print(res)
#结果
[20, 21, 22, 23]
大家猜一猜res的结果,不允许执行,不要偷看结果(假装没看见),选项如下:
A. res=[10,11,12,13] B. res=[11,12,13,14]
C. res=[20,21,22,23] D. res=[21,22,23,24]
如果有错误的地方请指正,感谢,持续更新中·····