生成器(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里面都会被清空。

Python——第四章:生成器(Generators)的更多相关文章

  1. 简学Python第四章__装饰器、迭代器、列表生成式

    Python第四章__装饰器.迭代器 欢迎加入Linux_Python学习群  群号:478616847 目录: 列表生成式 生成器 迭代器 单层装饰器(无参) 多层装饰器(有参) 冒泡算法 代码开发 ...

  2. [书籍翻译] 《JavaScript并发编程》第四章 使用Generators实现惰性计算

    本文是我翻译<JavaScript Concurrency>书籍的第四章 使用Generators实现惰性计算,该书主要以Promises.Generator.Web workers等技术 ...

  3. Python第四章-字典

    第四章 字典-当索引不好用时 4.0     字典可以理解成是C++里的map,可以映射任何类型.字典这种结构类型称为映射(mapping).   字典是Python中唯一内建的映射类型,字典中的值并 ...

  4. Python第四章实验报告

    一.实验项目名称:<零基础学Python>第四章的14道实例和4道实战 二.实验环境:IDLE(Python 3.9 64-bit) 三.实验目的和要求:熟练掌握Python序列的应用 四 ...

  5. Python 第四篇:生成器、迭代器、装饰器、递归函数与正则表达式

    一:生成器:Generator,可以理解为是一种一个函数产生一个迭代器,而迭代器里面的数据是可以通过for循环获取的,那么这个函数就是一个生成器,即生成器是有函数生成的,创建生成器使用()表示,比如g ...

  6. 新手Python第四天(生成器)

    Python 生成器 生成器和生成表达式 a=[i*2 for i in range(10)]#生成表达式 b=(i*2 for i in range(10))#生成器 生成器的特点:优点(不占用内存 ...

  7. 流畅的python第四章文本和字节序列学习记录

    字符问题 把码位转化成字节序列的过程是编码,把字节序列转化成码位的过程是解码 把unicode字符串当成人类可读的文本,码位当成机器可读的, 将字节序列编程人类可读是解码,把字符串编码成字节序列是编码 ...

  8. Python(四)生成器 和 杨辉三角

    学习链接: http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143177992 ...

  9. Python第四章(北理国家精品课 嵩天等)

    一.程序的分支结构 二.身体质量指数BMI #CalBIv1.py height,weight = eval((input("请输入身高(米)和体重\(公斤)[逗号隔开]:"))) ...

  10. python第四章:函数--小白博客

    Python函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可 ...

随机推荐

  1. 「sdoi2019 - D2T2」移动金币

    对 @command_block 没有 implementation 做法的细化.理论来说可以通过,但因为我实现得较劣无法通过.:( 把金币中的空隙看作石子,就是一个阶梯 Nim 的模型(有总共 \( ...

  2. 8月Node服务的3场事故

    有句话叫每一起严重事故的背后,必然有 29 次轻微事故和 300 起未遂先兆以及 1000 起事故隐患. 而我最近更是碰到了 3 起比较严重的线上事故,都是大意惹的祸. 一.数据库锁死 第一起事故发生 ...

  3. 区间检测(range)

    区间检测(range) 时间限制: 1 Sec  内存限制: 128 MB 题目描述 给定一个长度为n的序列,进行m次检测,每次检测某个区间中,是否有重复的数. 输入 第一行,两个整数n和m,表示序列 ...

  4. 再学Blazor——组件

    Blazor 应用基于组件,组件可以复用和嵌套.本文内容如下: 组件类 组件嵌套 组件参数 组件对象 1. 组件类 所有组件都是继承 ComponentBase 组件基类,razor 文件默认继承 C ...

  5. sql删除进程

    使用一下语句杀掉进程 USE master go DECLARE @dbname VARCHAR(200) SET @dbname = 'xxxx' --要关闭进程的数据库名 DECLARE @sql ...

  6. 🔥🔥你真的知道TCP协议中的序列号确认、上层协议及记录标识问题吗?

    引言 在前面的内容中,我们已经详细讲解了一系列与TCP相关的面试问题.然而,这些问题都是基于个别知识点进行扩展的.今天,我们将重点讨论一些场景问题,并探讨如何解决这些问题. 序列号确认问题 当A主机与 ...

  7. source insight 中添加指定类型文件

    以下为source insight 3.X版本的设置方法: source insight 中过滤某些格式的文件. 建立source insight工程后,先暂时不要急于添加文件. 打开options- ...

  8. 聊聊分布式 SQL 数据库Doris(三)

    详细内容阅读: Apache Doris 分区分桶新功能 与 数据划分. 在此基础上做总结与延伸. 在 Doris 的存储引擎规则: 表的数据是以分区为单位存储的,不指定分区创建时,默认就一个分区. ...

  9. python之range()、arange()和linspace()

    目录 range() arange() linspace() range() range()格式如:range(start, stop, step) start:开始的数值,默认从0开始 stop:结 ...

  10. C#/.NET/.NET Core推荐学习书籍(已分类)

    前言 古人云:"书中自有黄金屋,书中自有颜如玉",说明了书籍的重要性.作为程序员,我们需要不断学习以提升自己的核心竞争力.以下是一些优秀的C#/.NET/.NET Core相关学习 ...