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
    4
    list_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
    4
    list_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
    5
    list_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
    4
    list_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
    5
    list_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
    7
    list_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
    5
    dict_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
    5
    list_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
generator_use =(x*x for x in range(1,5))
print(generator_use)
print(generator_use.__next__(),end=" ")
print(generator_use.__next__(),end=" ")
print(generator_use.__next__(),end=" ")
print(generator_use.__next__(),end=" ")
print(generator_use.__next__(),end=" ")
# 运行结果:
# Traceback (most recent call last):
# File "D:\pycharmwork\blog_use.py", line 2330, in <module>
# print(generator_use.__next__(),end=" ")
# ^^^^^^^^^^^^^^^^^^^^^^^^
# StopIteration
# <generator object <genexpr> at 0x0000016F5D849150>
# 1 4 9 16

generator_use =(x*x for x in range(1,5))
for i in generator_use:
print(i,end=" ")
#运行结果:
# 1 4 9 16

判断其是否可迭代及其类型:

1
2
3
4
5
6
from collections.abc import *
print(isinstance((x*x for x in range(1,5)),Iterable))
print(type((x*x for x in range(1,5))))
#运行结果如下:
# True
# <class 'generator'>

嵌套for等语法同样可以用于生成器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
list_use1=["a","b","c"]
list_use2=["1","2","3"]
generator_use=(i+j for i in list_use1 for j in list_use2)
for i in generator_use:
print(i,end=" ")
# 运行结果:
# a1 a2 a3 b1 b2 b3 c1 c2 c3

dict_use={'a':1,'b':2,'c':3,'d':4}
generator_use=(x+str(y) for x,y in dict_use.items())
for i in generator_use:
print(i,end=" ")
# 运行结果:
# a1 b2 c3 d4

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
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
def get_1to5():
print("启动生成器函数")
for i in range(1,6):
print("生成一个新的值中......")
print("生成成功")
yield i
generator_use=get_1to5()
print(generator_use.__next__())
print(generator_use.__next__())
print(generator_use.__next__())
print(generator_use.__next__())
print(generator_use.__next__())
print(generator_use.__next__())
# 运行结果:
# Traceback (most recent call last):
# File "D:\pycharmwork\blog_use.py", line 2387, in <module>
# print(generator_use.__next__())
# ^^^^^^^^^^^^^^^^^^^^^^^^
# StopIteration
# 启动生成器函数
# 生成一个新的值中......
# 生成成功
# 1
# 生成一个新的值中......
# 生成成功
# 2
# 生成一个新的值中......
# 生成成功
# 3
# 生成一个新的值中......
# 生成成功
# 4
# 生成一个新的值中......
# 生成成功
# 5

for循环返回:

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
def get_1to5():
print("启动生成器函数")
for i in range(1,6):
print("生成一个新的值中......")
print("生成成功")
yield i
for i in get_1to5():
print(i)
# 运行结果:
# 启动生成器函数
# 生成一个新的值中......
# 生成成功
# 1
# 生成一个新的值中......
# 生成成功
# 2
# 生成一个新的值中......
# 生成成功
# 3
# 生成一个新的值中......
# 生成成功
# 4
# 生成一个新的值中......
# 生成成功
# 5

return对结果的影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def get_1to5():
print("启动生成器函数")
for i in range(1,6):
print("生成一个新的值中......")
print("生成成功")
yield i
return "错误源于完成了函数"
t =get_1to5()
print(t.__next__())
print(t.__next__())
# 运行结果:
# 启动生成器函数
# 生成一个新的值中......
# 生成成功
# 1
# Traceback (most recent call last):
# File "D:\pycharmwork\blog_use.py", line 2446, in <module>
# print(t.__next__())
# ^^^^^^^^^^^^
# StopIteration: 错误源于完成了函数

close显式停止函数运行:

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
def get_1to5():
print("启动生成器函数")
for i in range(1,6):
print("生成一个新的值中......")
print("生成成功")
yield i
generator_use = get_1to5()
print(generator_use.__next__())
print(generator_use.__next__())
generator_use.close()
print(generator_use.__next__())
# 运行结果:
# 启动生成器函数
# 生成一个新的值中......
# 生成成功
# 1
# 生成一个新的值中......
# 生成成功
# 2
# Traceback (most recent call last):
# File "D:\pycharmwork\blog_use.py", line 2468, in <module>
# print(generator_use.__next__())
# ^^^^^^^^^^^^^^^^^^^^^^^^
# StopIteration

def get_1to5():
print("启动生成器函数")
for i in range(1,6):
print("生成一个新的值中......")
print("生成成功")
yield i
generator_use = get_1to5()
for i in generator_use:
print(i)
if i == 3:
generator_use.close()
# 运行结果:
# 启动生成器函数
# 生成一个新的值中......
# 生成成功
# 1
# 生成一个新的值中......
# 生成成功
# 2
# 生成一个新的值中......
# 生成成功
# 3

send发送信息和receive接收信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def get_1to5():
for i in range(1,6):
val =yield i
print("接收信息"+str(val))

generator_use = get_1to5()
i = generator_use.__next__()
print(i)
while True:
i = generator_use.send(i*i)
print(i)
if i == 5:
break
# 运行结果:
# 1
# 接收信息1
# 2
# 接收信息4
# 3
# 接收信息9
# 4
# 接收信息16
# 5

最后用生成器函数设计一个杨辉三角:

1
2
3
4
5
6
7
8
9
10
11
def Triangle():
result=[1]
result_next = [1,1]
while True:
yield result
result = result_next
result_next=[1]+ [result_next[i]+result_next[i+1] for i in range(len(result_next)-1)]+[1]
for i in Triangle():
print(i)
if(len(i)==10):
break

python进阶——高级特性(三)列表生成式、生成器
https://github.com/xiaohei07/xiaohei07.github.io/2023/07/13/python进阶——高级特性(三)列表生成式、生成器/
作者
07xiaohei
发布于
2023年7月13日
许可协议