python进阶——高级特性(三)列表生成式、生成器
本文最后更新于:1 年前
¶(五)列表生成式
¶1. 概述:
列表生成式是Python中内置的创建列表的方法。
列表生成式基于可迭代对象创建,相较于for循环和if循环,创建快捷(基于C语言实现),代码简介,更具可读性。
但要注意列表生成式对于简单的任务处理更具优势,如果情况复杂,生成难度可能会很高;而且列表生成式会一次性生成全部列表元素,对于大型列表,会导致内存被大量占用。
列表生成式是Python的高级特性体现之一。
¶2. 语法格式:
[expression for item in iterable if conditional]
expression:expression对应生成式列表的元素,是生成后需要保存的内容,expression通常利用for循环内的变量来计算表示,expression除了使用range循环外,不可省略。
注意:for循环前面的if...else语句是算入表达式之中的,因此for循环前面的if条件语句必须有else来保证能够取到元素。
for循环:语法对应基本的for循环,项item对应了可迭代对象iterable的每一个元素。
允许使用range循环直接表示,可以进行for循环的迭代嵌套创建新的对应关系作为生成式的元素,循环的item可以使用多个变量。
if条件语句:用于判断循环的每个元素是否符合生成的要求标准,用于筛选循环的元素,因此不能在后面的if判断中使用else,否则失去了筛选的作用。
¶3. 例子:
-
首先是没有if语句时的列表生成式:
- 直接使用range迭代快速构建1-10的列表,需要进行list类型转化:
1
2
3
4list_use = list(range(1,11))
print(list_use,end=" ")
# 运行结果:
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]生成一个n*n为元素的1-10的列表:
1
2
3
4list_use = [x*x for x in range(1,11)]
print(list_use,end=" ")
# 运行结果:
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]根据给定列表字符串集生成每个字符串的长度:
1
2
3
4
5list_use = ["ab","cde","fghi","jklmn"]
list_use2=[len(x) for x in list_use]
print(list_use2,end=" ")
# 运行结果:
# [2, 3, 4, 5] -
带有if语句时的列表生成式:
生成30以内的偶数列表:
1
2
3
4list_use = [x for x in range(1,31) if x%2==0]
print(list_use,end=" ")
# 运行结果:
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]生成指定字符串中长度大于3的字符串并全部大写:
1
2
3
4
5list_use = ["ab","cde","fghi","jklmn"]
list_use2=[x.upper() for x in list_use if len(x)>3]
print(list_use2,end=" ")
# 运行结果:
# ['FGHI', 'JKLMN'] -
多个for循环嵌套的列表生成式:
全排列的生成:
1
2
3
4
5
6
7list_use1=["a","b","c"]
list_use2=["1","2","3"]
list_use=[i+j for i in list_use1 for j in list_use2]
print(list_use,end=" ")
# 运行结果:
# ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3'] -
for循环使用多个变量的列表生成式:
经常使用的是把dict的键值对按某种逻辑组合成列表的一个元素:
1
2
3
4
5dict_use={'a':1,'b':2,'c':3,'d':4}
list_use=[x+str(y) for x,y in dict_use.items()]
print(list_use,end=" ")
# 运行结果:
# ['a1', 'b2', 'c3', 'd4']也可以直接对list的内部元组进行组合:
1
2
3
4
5list_use1=[("a","1"),("b","2"),("3","c")]
list_use=[i+j for i,j in list_use1]
print(list_use,end=" ")
# 运行结果:
# ['a1', 'b2', '3c']
¶(六)生成器
对于列表生成式,虽然其运行效率高,代码简洁,但是其一次性生成整个列表,内存消耗代价大,因此,为了节约内存,我们可以使用生成器生成列表。
当然,生成器也可以用于生成其他类型。
¶1. 概念:
生成器(generator)是一种返回一个值的迭代器,每次从该迭代器取下一个值。
生成器实际上是一种高级迭代器,因此也是可迭代对象,可以使得需要返回一系列元素的函数所需的代码更加的简单和高效,当然也因此只能遍历一次。
生成器通过某种算法,在循环中能够不断计算处下一次需要生成的元素,而不需要创建完整的生成列表,以此来节省空间。
注意,生成器实际上保存的是算法,调用next方法时能够就能够计算出下一个元素的值。
一旦计算到最后一个元素,再次调用next方法,就会抛出StopIteration错误。
生成器有两种表示:生成器表达式和生成器函数。
¶2. 生成器表达式:
生成器表达式也被称为隐式生成器,语法与推导式基本相同,但要用圆括号来创建生成器,也就是将其[]换成()。
生成器表达式会产生新的生成器对象并返回,不会构建出一个列表进行返回,所以直接输出生成器不会输出对应的结果。
另外,生成器表达式禁止使用yield和yield from表达式(后文会讲)。
例子如下:
将上方例子的[]直接更换为()即可获得生成器,然后使用时直接用next函数或者遍历即可
1 |
|
判断其是否可迭代及其类型:
1 |
|
嵌套for等语法同样可以用于生成器:
1 |
|
¶3. 生成器函数:
生成器函数类似一般函数,但是额外增加了yield关键字,此时函数返回的是一个生成器对象。
生成器函数和一般函数的区别在于,一般函数的执行流程是顺序执行,当遇到return语句或者执行到最后一行语句时才会返回,而对于生成器函数,遇到yield语句会暂停函数的执行并返回此时的中间结果;一般函数的调用是用函数名显式调用,而生成器函数的调用必须显式或者隐式的使用next语句调用,同时恢复到上一次的yield语句处再往下继续执行。
也就是说,生成器函数能够利用yield语句暂停函数的执行并返回中间元素的对应生成器,并通过next方法恢复函数中断现场,并从暂停的语句处开始继续执行,直到没有yield语句可以运行,此时会引发StopIteration异常。
**yield语句:**语法格式同return语句,只替换关键字。
yield语句负责产生一个返回值并返回,返回后生成器函数会在yield语句处保存函数的内部状态,并挂起函数,直到再次调用,从上一次yield的地方继续执行,沿用上一轮的函数内部变量的状态。
注意,return语句对生成器函数返回值不会产生任何影响,函数返回值仍为生成器对象。如果没有return语句的话,注意函数结束时返回的是StopIteration异常,如果有return语句,函数会立刻停止运行并返回StopIteration异常,而return的返回值只作为异常的说明,而不是函数的返回值。
可以直接显式调用close()来手动关闭生成器函数。
可以用send()向生成器传入外部的值,但注意不能在启动生成器函数时传入。用一个变量接收send的值。
接收语法为:receive变量名 = yield value,这里send的信息会被赋给receive变量名。
send执行时会运行函数,所以如果需要send信息请注意增加yield语句或者修改外部循环。
生成器函数常用于使用生成器的流数据缓冲区。
例子如下:
简单的生成器函数,用next方法显式返回12345序列:
1 |
|
for循环返回:
1 |
|
return对结果的影响:
1 |
|
close显式停止函数运行:
1 |
|
send发送信息和receive接收信息:
1 |
|
最后用生成器函数设计一个杨辉三角:
1 |
|