生成器、列表推导式和生成器表达式

生成器

生成器特点:

1. 生成器本质上迭代器

2. 生成器是自己用代码创建的

获取生成器的两种方式:

1. 生成器函数

2. 生成器表达式

 

yield

下面来看一个例子

 1 def func():
 2     print(111)
 3     yield 666
 4     print(222)
 5     yield 233333
 6 
 7 
 8 ret = func()    # 获取生成器
 9 print(ret)      # <generator object func at 0x000002B1E535B258>
10 print(type(ret))    # <class 'generator'>

把函数的return换成yield就可以把函数变成生成器,变成生成器后再读到函数名加括号后就不再表示执行函数而是表示获取一个生成器。变成生成器后就可以用next来取值了,来看代码

print(next(ret))   # 一个next执行一个yield(和yield之前的)

执行结果

111
666

再继续取值

1 print(next(ret))   # 111 666, 一个next执行一个yield(和yield之前的)
2 print(next(ret))   # 222 233333 
3 print(next(ret))     # 超出,报错

生成器函数牢记一点:一个next只执行一个yield语句和此yield语句与上一个yield语句之间的语句

如果将yield语句赋值给某个变量会怎样,来看代码

 1 def func():
 2     print(111)
 3     a = yield 666
 4     print("a1:", a)
 5     print(222)
 6     yield 888
 7     print("a2:", a)
 8     yield 999
 9 
10 
11 ret = func()

执行一个next

print(next(ret))

执行结果

111
666

执行两个next

111
666
a1: None
222
888

执行三个next

print(next(ret))   
print(next(ret))
print(next(ret))

结果

111
666
a1: None
222
888
a2: None
999

分析上述代码我们可以得出规律:

1. yield语句是将yield后面的值返回给next()语句

2. yield语句本身没有返回值

3. 每执行一个next语句就从上一个yield语句(不包括)开始从上往下执行

yield与return的异同:

1. return会终止函数,yield不会终止函数

2. yield和return都有返回值,yield给next()返回值,return给函数名()返回值

 

send

生成器函数还有一个方法:send(),send与next类似,也可以一个一个地取值,但是send还有个作用,可以传值给上一个yield(括号里写None就跟next一样)

 1 def func():
 2     a = yield 111
 3     print(a)
 4     print("mandy")
 5     b = yield 222
 6     print(b)
 7     print("sa")
 8     yield 333
 9     yield "hanser"
10 
11 
12 gen = func()   # 获取生成器
13 # sen不仅能对应yield取值,而且可以给上一个yield语句发送一个值
14 
15 print(gen.send(None))   # 第一个send不能传值(传值为None,只取值)    111
16 print(gen.send(666))   # 两个作用:1.把666传给a;2.取值222.    666  mandy  222
17 print(gen.send("yousa"))                               # yousa  sa  333
18 print(next(gen))                                       # hanser

执行结果

111
666
mandy
222
yousa
sa
333
hanser

注意send(value)是将value传给上一个yield的yield  值这个语句,如果函数里有将yield  值赋值给某个变量,就要注意变量值的变化。代码的执行顺序是从上一个yield的后面开始,从上往下执行,最后执行当前的yield(next)。还有两点点需要注意:

1. 第一次取值不能用send传参

2. 最后一个yield永远得不到send传的值

yield from

前面的yield的返回值都是数字,如果yield的返回值是可迭代对象会有什么不同呢,一起来看

1 def func():
2     lst = [1, 2, 3, 4]
3     yield lst
4 
5 
6 gen = func()     # gen [[1, 2, 3, 4]]
7 print(gen)

执行结果

<generator object func at 0x0000016A300AFF68>
[1, 2, 3, 4]

用next得到的结果是一个列表,如果不用next取值,直接用for循环取值,能取到吗?来看

for i in next(gen):
    print(i)

结果

<generator object func at 0x0000024B4905FF68>
1
2
3
4

由此可以可以知道gen这个生成器类似于一个列表,里面只有一个元素也是列表,即 [[1, 2, 3, 4]],

如果把yield换成yield from,结果会是怎样的呢

1 def func():
2     lst = [1, 2, 3, 4]
3     yield from lst        # 把lst变成迭代器
4 
5 
6 gen = func()
7 print(next(gen))
8 print(next(gen))
9 print(next(gen))

执行结果

1
2
3

列表推导式

定义:列表推导式就是用一行代码构建简单的列表

列表推导式的三种模式

1. 循环模式

语法:[变量(加工后的变量) for 变量 in iterable]

[i for i in range(10)]

2. 筛选模式

语法:[变量(加工后的变量) for 变量 in iterable if 条件]

[i for i in range(10) if i % 2 == 0]
["地球%s号" % i for i in range(1, 50) if i % 3 == 0]

3. 三元模式(循环模式与三元运算的结合)

lst = ["$" if i % 2 == 0 else i for i in [i**2 for i in range(10)]]
print(lst)

执行结果

['$', 1, '$', 9, '$', 25, '$', 49, '$', 81]

优点:节省代码

缺点:(1)只有一行,debug查不出来

   (2)不能构建复杂的列表(常规的方法可以)

生成器表达式

生成器表达式语法与列表推导式几乎一样,只是把[]换成()

1 gen = ("嫦娥%s号" % i for i in range(1, 10))
2 print(gen)
3 print(next(gen))  # 嫦娥1号
4 for i in gen:
5     print(i)     # 嫦娥2号 嫦娥3号...

执行结果

<generator object <genexpr> at 0x0000016C3625FF68>
嫦娥1号
嫦娥2号
嫦娥3号
嫦娥4号
嫦娥5号
嫦娥6号
嫦娥7号
嫦娥8号
嫦娥9号

什么时候用列表推导式,什么时候用生成器表达式?

数据量小的时候用列表推导式,数据量大的时候用生成器表达式,因为生成器表达式可以一个一个地取值,不占内存

字典推导式和集合推导式

字典推导式

字典推导式的语法和列表推导式类似,只是把变量换成键值对,[]换成{}

print({i:None for i in range(1, 10)})

结果

{1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}

键值交换

# 键值交换
m = {"a": 10, "b": 34, "c": 88}
print({v: k for k, v in m.items()})

执行结果

{10: 'a', 34: 'b', 88: 'c'}

集合推导式

set2 = {i*3+1 for i in range(10)}
print(set2)

执行结果

set2 = {(i*3+1) % 4 for i in range(10)}
print(set2)

执行结果

{0, 1, 2, 3}

猜你喜欢

转载自www.cnblogs.com/zzliu/p/10214359.html