python -生成器

1、生成器对象

image

  • 定义:生成器其实是特殊的迭代器,可以称为自定义的迭代器,定义阶段为一个普通的函数,只有在调用的时候才将函数变成了生成器,第一次调用函数的时候并不会执行函数体代码,只是转换。
  • 关键字: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传值

image

用到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、生成器表达式

image

如何创建生成器?

  • 列表生成式前面的文章写到过,这里的生成器只需将列表生成式的中括号改成小括号。

如何获取生成器的元素?有两种方法

  • 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]


如果有错误的地方请指正,感谢,持续更新中·····
image

posted @ 2021-11-22 21:22  HammerZe  阅读(72)  评论(0编辑  收藏  举报