day12 Python 生成器

一. 生成器

什么是⽣成器. 生成器实质就是迭代器. 在python中有三种⽅式来获取⽣成器:

  1. 通过生成器函数
  2. 通过各种推导式来实现⽣成器
  3. 通过数据的转换也可以获取⽣成器

首先, 我们先看一个很简单的函数:

def func():
    print("111")
    return 222
ret = func()
print(ret)

结果: 111 222

将函数中的return换成yield就是生成器

def func(): print("111")
    yield 222
    ret = func()
print(ret)

结果:
<generator object func at 0x10567ff68>
 

运行的结果和上面不⼀样. 为什么呢. 由于函数中存在了了yield. 那么这个函数就是一个生成器函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. ⽽是获取这个生成器. 如何使用呢? 想迭代器. ⽣成器的本质是迭代器. 所以. 我们可以直接执行__next__()来执行

以下⽣生成器.

def func(): 
    print("111")
    yield 222

gener = func() # 这个时候函数不会执行. 而是获取到⽣成器
ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回数据
print(ret)
结果:
111
222

那么我们可以看到, yield和return的效果是一样的. 有什区别呢? yield是分段来执⾏行行⼀一个 函数. return呢? 直接停⽌止执⾏行行函数.

 

接下来我们来看send方法, send和__next__()⼀样都可以让生成器执行到下一个yield.

def eat():
    print("我吃什么啊") 
    a = yield "馒头" 
    print("a=",a)
    b = yield "⼤饼"
    print("b=",b)
    c = yield "韭菜盒⼦子" 
    print("c=",c) 
    yield "GAME OVER"
 

gen = eat() # 获取生成器 
ret1 = gen.__next__() 
print(ret1)
ret2 = gen.send("胡辣汤") 
print(ret2)

ret3 = gen.send("狗粮") 
print(ret3)
ret4 = gen.send("猫粮") 
print(ret4)
 

send和__next__()区别:

1. send和next()都是让生成器向下走一次
2. send可以给上一个yield的位置传递值,不能给最后一个yield发送值.在第⼀次执⾏生成器代码的时候不能使⽤send() ⽣成器可以使⽤for循环来获取内部的元素:

def func(): 
    print(111)
    yield 222
    print(333)
    yield 444
    print(555)
    yield 666

gen = func()
for i in gen:
    print(i)

结果: 
111 222 333 444 555 666

⼆. 列表推导式, ⽣成器表达式以及其他推导式 ⾸先我们先看⼀下这样的代码, 给出一个列表, 通过循环, 向列表中添加1-13 :替换成列表推导式:

列表推导式是通过⼀行来构建你要的列表, 列表推导式看起来代码简单. 但是出现错误之后很难排查.
列表推导式的常⽤写法:

[ 结果 for 变量量 in 可迭代对象]
例. 从python1期到python14期写入列列表lst:

lst = [] 
for i in range(1, 15):
    lst.append(i)

print(lst)

lst = [i for i in range(1, 15)]
print(lst)

lst = ['python%s' % i for i in range(1,15)]
print(lst)

筛选模式:
[ 结果 for 变量量 in 可迭代对象 if 条件 ]

⽣成器表达式和列表推导式的语法基本上是一样的. 只是把[]替换成()

打印的结果就是一个生成器. 我们可以使用for循环来循环这个生成器:

# rst = [x for x in "abc"]
#
# print(rst)
#
# lst1 = ["中岛美雪", "夏川美里", "原由子", "汪峰", "田震", "那英", "周杰伦"]
#
# lst2 = [x for x in lst1 if len(x) < 3]
#
# print(lst2)
#
# lst3 = [x * x for x in range(1, 21) if x % 2 == 1]
#
# print(lst3)
#
# lst4 = [3, 6, 9]
#
# lst5 = [(x - 2, x - 1, x) for x in lst4]
#
# print(lst5)
#
# lst6 = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
#         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
#
# lst7 = [j for i in lst6 for j in i if j.count("l") == 2]
#
# print(lst7)
#
# lst8 = {"张无忌": "赵敏", "杨过": "小龙女", "郭靖": "黄蓉"}
#
# lst9 = {v: k for k, v in lst8.items()}
#
# print(lst9)
#

生成器表达式和列表推导式的区别:

1. 列表推导式比较耗内存.⼀次性加载.生成器表达式几乎不占用内存.使用的时候才分配和使用内存

2. 得到的值不一样.列表推导式得到的是⼀个列表.生成器表达式获取的是⼀个生成器.

⽣成器的惰性机制: ⽣成器只有在访问的时候才取值. 说白了. 你找他要他才给你值. 不找他要. 他是不会执⾏的.

# def abc():
#     print("-->",111)
#     yield 222
#     yield  333
#
# g = abc()
# g1 = (x for x in g)
# g2 = (x for x in g1)
#
#
# print("g =", list(g))
#
# print("g1 =", list(g1))
#
#
# print("g2 =", list(g2))

  

总结: 推导式有, 列表推导式, 字典推导式, 集合推导式, 生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选) ⽣成器表达式可以直接获取到生成器对象. 生成器对象可以直接进行for循环. ⽣成器具有惰性机制.

一个⾯面试题. 难度系数500000000颗星:

def add(a, b): 
    return a + b

def test():
    for r_i in range(4):
        yield r_i

g = test()

# 此处只有print的时候才是真正的调用了生成器,所以此处循环两次只是生成了两个生成器表达式,n值在循环的时候没有替换到生成器表达式之中。
# 所以循环两次之后的结果为:g=(add(n, i) for i in (add(n, i) for i in g)) 而第二次循环的n=10,所以把10替换掉n,结果为20,21,22,23. for n in [2, 10]: g = (add(n, i) for i in g) # print(list(g))

  

友情提示: 惰性机制, 不到最后不会拿值

猜你喜欢

转载自www.cnblogs.com/fanghongbo/p/9891440.html
今日推荐