Python-列表推导式、生成器-Python自学笔记5
本章重点
- 列表生成式
- 循环模式
- 筛选模式
- 嵌套模式
- 生成器
- 什么是生成器
- 生成器的特点是什么
- 怎么创建一个生成器
- 生成器表达式
- 生成器的优点
列表推导式
列表推导式就是for循环的简写,能生成比较复杂但是有规律的列表
列表推导式有三种模式分别是 循环模式 筛选模式 嵌套模式
-
循环模式
-
[最终结果 for 每次遍历获得的元素 in 需要遍历的对象]
-
实例:
-
li = [num for num in range(0, 101)] # 这是列表推导式 # 相当于 # li = [] # for num in range(0, 101): # li.append(li)
-
结果:
print(li)
-
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
-
-
实例2:
-
s1 = "abcdefg" str_list = [s.upper() for s in s1] # 我们可以操作最终结果,让最终结果成为我们想要的样子,这里我想让最终结果每一个都是大写 # 相当于 # str_list = [] # for s in s1: # str_list.append(s.upper())
- 结果:
['A', 'B', 'C', 'D', 'E', 'F', 'G']
总之列推导式不需要去死记硬背,只需要多敲几遍就熟悉了。
- 结果:
-
筛选模式
-
[最终结果 for 每次遍历获得的元素 in 需要遍历的对象 筛选条件]
-
实例:
-
# 目标 获取 1到100的所有偶数并添加到列表里 li = [num for num in range(1, 101) if num % 2 == 0]
-
结果:
print(li)
-
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
-
-
嵌套模式
-
[最终结果 for 外层遍历获得的元素 in 外层遍历对象 for 内层遍历获得的元素 in 外层遍历获得的元素]
-
实例:
-
# 目标遍历[[1, 2, 3, 4], [5, 6], [7, 8, 9, 0]]获得里面的元素并添加到新列表 l = [[1, 2, 3, 4], [5, 6], [7, 8, 9, 0]] li = [num for i in l for num in i] # 相当于 # for i in l: 外层循环 # for num in i: 内层循环 # li.append(num) 将每次遍历获得的元素添加到指定列表里
-
结果:
print(li)
-
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
-
嵌套模式也支持筛选
-
实例2:
-
# 目标将s_list列表中包含Python的字符串添加到新列表里,并且全部转为大写 s_list = [['Pythonacbdef', 'asdasPython', 'abc'], ['sffsdopython', 'sdasd'], ['psasdnodython', 'cdf', 'pythonnb']] new_list = [s.upper() for l in s_list for s in l if 'PYTHON' in s.upper()] # 相当于 # for l in s_list: # 遍历外层列表 # for s in l: # 遍历大列表内的小列表 # if 'PYTHON' in s.upper(): # 这里的upper是无视大小写判断字符串里是否存在PYTHON, 与转换结果的大写无关 # new_list.append(s.upper()) # 将存在python的字符串转换成大写追加进新列表里
-
结果:
print(new_list) >>> ['PYTHONACBDEF', 'ASDASPYTHON', 'SFFSDOPYTHON', 'PYTHONNB']
-
可能有点绕,我画了个图将列表推导式里的段代码与
for
循环一一对应的标注了出来,稍微看一下就好,具体还得自己多敲几遍,实践是理解的最好办法。比如给自己制定点题目,如何自己尝试使用列表推导式解出来。 -
-
生成器
什么是生成器
生成器是一直特殊的迭代器,迭代器能做的事生成器也能做,生成器本质上就是迭代器,只不过生成器的编写方式和表现形式不一样,迭代器是由可迭代对象转换而来,生成器是我们自己用生成器函数或生成器表达式创建出来的。迭代器能做的事情生成器全部能做到,且特性也与迭代器一样,不能直接查看生成器里的元素,需要使用next()
进行取值,在没有后续元素时,继续next()
也会和迭代器一样抛出一个 StopIteration
异常。
生成器的特点是什么
生成器的特点也和迭代器一样,只能遍历一次,每次只取一个元素,当取下一个元素时本次占用内存的元素就会被清除,也就是说在同一时间里只占用一小块内存,生成器的元素用完即清除,非常节省内存。
怎么创建一个生成器
创建生成器有两种方法,分别是 生成器函数
生成器表达式
-
生成器函数
-
生成器函数的写法跟我们函数的写法几乎一模一样,不同的是函数返回值使用的是
return
并且在使用后就退出函数了,且生成器函数和函数的执行顺序也不一样,这个后面会说到,先看看怎么写一个生成器吧 -
def gene(): """生成器""" li = [1, 2, 3] # 假设此处如果是 yield li 那么使用生成器对象取值,取到的就是整个列表[1, 2, 3] for i in li: yield i gene_obj = gene() # 获取生成器对象 print(next(gene_obj)) # 生成器不能直接查看元素需要进行next取值 print(next(gene_obj)) print(next(gene_obj))
-
结果:
-
1 2 3
-
假设我们在上面
print(next(gene_obj))
后再print(next(gene_obj))
一下看看会怎么样 -
结果:
-
Traceback (most recent call last): File "C:/Users/xxxx/xxxx/xxxx/xxxx.py", line 10, in <module> print(next(gene_obj)) StopIteration
-
前面提到了生成器和迭代器一样,取值次数超过元素总个数,就会抛出
StopIteration
异常
-
-
生成器函数 yield from 使用方法
- 还是拿上面的例子举例
def gene():
li = [1, 2, 3]
# yield from li 就是每次使用next取值一次就返回列表的一个元素
yield from li # 可以代替 for i in li 简化代码增加代码的可读性
gene_obj = gene() # 获取生成器对象
print(next(gene_obj)) # 生成器不能直接查看元素需要进行next取值
print(next(gene_obj))
print(next(gene_obj))
"""
结果:
1
2
3
"""
-
生成器对象也是一个可迭代对象,可以使用前面提到的
print('__iter__' in dir(gene_obj))
查看,是可迭代对象就意味着可以使用for
进行取值 -
尝试一下
-
def gene(): """生成器""" li = [1, 2, 3] yield from li gene_obj = gene() # 获取生成器对象 for i in gene_obj: print(i)
-
结果:
-
1
-
2
-
3
-
生成器函数的执行顺序
-
生成器的工作顺序就是
遇到yield 就保存状态 返回元素
---再次next() 回到上次保存状态的位置继续往下执行
一直按照这两个状态运行,直到生成器内没有元素可取了,生成器的使命也就完成了。 -
也就是说生成器函数的执行顺序是遇到
next()
执行 执行到yield
返回元素并保存状态,可以理解为代码停留在yield
的下一行,但是不执行,等到下一次next()
就从上一次停留的位置进行往下执行 -
-
像上图中的代码运行结果是
-
1 我执行了没有 1 2 我执行了没有 2 3
-
-
生成器表达式
-
生成器表达式与列表推导式的写法几乎一模一样
- 列表推导式: [最终结果 for 每次遍历获得的元素 in 需要遍历的对象]
- 生成器表达式:(最终结果 for 每次遍历获得的元素 in 需要遍历的对象)
- 对的只是中括号换成了小括号
-
实例:
-
gene_obj = (i for i in range(0, 4))
这样我们就获得了一个生成器对象 -
# 来next()一下这个生成器对象 print(next(gene_obj)) print(next(gene_obj)) print(next(gene_obj)) """ 结果: 0 1 2 """
-
从上面的代码我们可以看出来,生成器还有另外一个好处就是,它只在你需要的时候从产生一个元素,可以实现一边循环一边计算,并不会一次性生成所有结果,也就避免了爆内存的情况出现。
-
一直都说生成器节省内存,但是大家都没有什么直观的感受,下面来两段代码可以打开终端窗口 输入
python
体验一下(可以按 ctrl + C 终止程序)sum([i**i for i in range(0, 1000000)])
sum((i**i for i in range(0, 1000000)))
- 我们可以看到用列表表达式的内存占用还在随着程序的运行而增加,而生成器的内存占用一直很低。
-
-
-
生成器优缺点
- 优点
- 节省内存,这是生成器最大的优点没有之一
- 延迟计算,一次返回一个结果。
- 增加代码可读性(这个我觉得看个人吧)
- 缺点:
- 生成器对象只能遍历一次
- 时间上的消耗会稍微多一点(用时间换空间嘛)
- 优点
-
总结:
-
生成器NB, Python NB
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具