Python——第四章:生成器(Generators)

生成器(generator):
    生成器的本质就是迭代器

    创建生成器的两种方案:
        1. 生成器函数
        2. 生成器表达式

    生成器函数
        生成器函数中有一个关键字yield
        生成器函数执行的时候, 并不会执行函数, 得到的是生成器.

        yield: 只要函数中出现了yield. 它就是一个生成器函数
            作用:
                1. 可以返回数据
                2. 可以分段的执行函数中的内容, 通过__next__()可以执行到下一个yield位置
        优势:
            用好了, 特别的节省内存


    生成器表达式 -> 一次性的
        语法: (数据 for循环 if)

 


正常的函数调用如下:

def func():
    print(123456)
    return 999

ret = func()
print(ret)

#运行结果
123456
999

使用生成器命令yield替代return。生成器函数执行的时候,并不会执行函数,得到的是生成器。

def func():
    print(123456)
    yield 999  # yield也有返回的意思.

ret = func()
print(ret)

#运行结果
<generator object func at 0x115f2dbd0>

生成器的本质就是迭代器,因此我们可以用迭代器的模式使用它:

def func():
    print(123456)
    yield 999  # yield也有返回的意思.

ret = func()
print(ret.__next__())  # yield只有执行到next的时候才会返回数据

#运行结果
123456
999

上面运行结果中:123456是函数的正常执行,999是yield返回的值,也就是说999是print()打印出来的,我们把print()拿掉就可以看到只有123456了

def func():
    print(123456)
    yield 999  # yield也有返回的意思.

ret = func()
ret.__next__()
#运行结果
123456

因此我们可以发现,yieldreturn是有一些区别的:return的用法是立即执行函数,并返回数据,而yield是只有执行到next的时候,才会返回数据。

如果我们执行2次next()又会出现和迭代器一样的StopIteration报错

def func():
    print(123456)
    yield 999  # yield也有返回的意思.

ret = func()

print(ret.__next__())  # yield只有执行到next的时候才会返回数据
print(ret.__next__())  # StopIteration

#运行结果
123456
999
Traceback (most recent call last):
  File "D:\迭代器.py", line 8, in <module>
    print(ret.__next__())  # StopIteration
          ^^^^^^^^^^^^^^
StopIteration

这也证明了:" 生成器的本质就是迭代器"

 

yield可以让程序分段的执行函数中的内容, 通过__next__()可以执行到下一个yield位置

执行一次:

def func():
    print(123)
    yield 666
    print(456)
    yield 999

ret = func()
print(ret.__next__())

#运行结果
123
666

执行两次:

def func():
    print(123)
    yield 666
    print(456)
    yield 999

ret = func()
print(ret.__next__())
print(ret.__next__())

#运行结果
123
666
456
999

这里yield就明显区别与return:当使用return的时候(比如return 666),return后面的内容全部都不会再执行。

去工厂定制10000件衣服

def order():
    lst = []
    for i in range(10000):
        lst.append(f"衣服{i}")
    return lst

lst = order()
print(lst)

简单的for循环会一次性生产出10000件衣服,并且出现了大列表lst[0,1,2,3,……,9999]这样会严重占用内存,并且双方都不好处理。

为此我们使用生成器玩法,让每次出货量为100件,每次执行__next__后再生产100件,这样就非常不占用内存,并且生产压力和库存压力都减少很多。

def order():
    lst = []
    for i in range(10000):
        lst.append(f"衣服{i}")
        if len(lst) == 100:
            yield lst
            # 下一次拿数据
            lst = []    #清空计数器重新计数


gen = order()
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())

还可以用一个列表去接收这些yield的值:

def order():
    lst = []
    for i in range(10000):
        lst.append(f"衣服{i}")
        if len(lst) == 100:
            yield lst
            # 下一次拿数据
            lst = []    # 清空计数器重新计数

gen = order()

# 用于追加结果的列表
result_list = []

# 逐次执行生成器并追加结果到列表中
result = next(gen)
result_list.append(result)

result = next(gen)
result_list.append(result)

result = next(gen)
result_list.append(result)

result = next(gen)
result_list.append(result)

# 打印包含所有结果的列表
print(result_list)

以下这段代码,存在的意义就是,使用生成器的yield特性,将程序分段执行,这样就可以避免庞大的数据执行时,严重占用内存的现象。

def order():
    lst = []
    for i in range(10000):
        lst.append(f"衣服{i}")
        if len(lst) == 100:
            yield lst
            # 下一次拿数据
            lst = []    # 清空计数器重新计数

gen = order()

# 使用 list() 函数接收生成器的 yield 返回值
result_list = list(gen)

在处理大型数据集或者生成器产生的数据量很大时,一次性获取所有值可能会导致内存占用较大,因为所有值都需要同时存储在内存中。在这种情况下,你可能希望逐个获取生成器的值,以减小内存压力。

如果数据量较大或者你希望逐个获取生成器的值,那么逐次调用 next(gen) 会更有优势!

这就是生成器的存在的价值。

生成器表达式

生成器表达式 -> 一次性的
    语法: (数据 for循环 if)

一次一次的获取生成器的返回值

gen = (i**2 for i in range(10))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

#执行结果
0
1
4
9

因为生成器本质就是迭代器,可以被for循环迭代,因此:

gen = (i**2 for i in range(10))
for item in gen:
    print(item)
    
#执行结果
0
1
4
9
16
25
36
49
64
81

如果要把所有的数据拿出来变成列表,就可以直接用list()套上操作

gen = (i**2 for i in range(10))

lst = list(gen)
print(lst)

#执行结果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

这里有个隐藏的list循环迭代操作

s = list("周杰伦")  # list() =>  for  => next()
print(s)

#执行结果:
['周', '杰', '伦']

这里证明了:list里存在着一个for循环,里面也有遍历所有的next()操作

 

*****最后这里有个需要注意的地方——如果gen里面的数据全被for循环使用过后,再次使用就是空的

gen = (i**2 for i in range(10))

for item in gen:
    pass
lst = list(gen)
print(lst)

#执行结果
[]

生成器表达式是一次性的!只能被用一次!

gen = (i**2 for i in range(10))

print(next(gen))
print(next(gen))
print(next(gen))

lst = list(gen)
print(lst)

#运行结果:
0
1
4
[9, 16, 25, 36, 49, 64, 81]

生成器本身是迭代器。迭代器被print(next(gen))拿过3次后,前三个数(0、1、4)就会消失。仅剩下后面的数据,后面的数据一旦被拿完,gen里面都会被清空。

posted @   Magiclala  阅读(147)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示