生成器和生成器表达式

一.生成器
1.生成器
  • 生成器的本质就是迭代器
  • 生成器的特点和迭代器一样, 取值方式和迭代器一样
  • 生成器一般由生成器函数或者生成器表达式来创建
  • 生成器说白了就是手写的迭代器
 
2.生成器函数
  • 和普通函数没有区别, 里面有yield的函数就是生成器函数.
  • 生成器函数在执行的时候, 默认不会执行函数体, 返回的是生成器.
  • 通过生成器的__next__()分段执行这个函数.
  • send()和__next__()功能一样, 唯一的区别是send()可以给上一个yield的位置传值.
  • send()不能在开头,最后一个yield也不可以用send()
 
# 生成器函数
def func():
    print("娃哈哈")
    yield 111        # 函数中有yield 这就是生成器函数
 
ret = func()         # 生成器, 这里不会执行函数的, 拿到的结果是生成器
print(ret)             # 结果: <generator object func at 0x000001AB93D4FE60>
 
def func():
    print("这是第一个")
    yield 111
    print("这是第二个")
    yield 222
    print("这是最后一个")
    yield 333
 
ret = func()
print(ret.__next__())    # 执行函数, 执行到下一个yield,
print(ret.__next__())    # 继续执行到下一个yield
print(ret.__next__())    # 继续执行到下一个yield
print(ret.__next__())    # 报错, 错误信息StopIteration
 
send()和__next__()
效果一样, 只是send()可以给上一个yield的位置传值.
send()不能在开头
 
def func():
    print("这是第一个")
    a = yield 111
    print(a)
    print("这是第二个")
    b = yield 222
    print(b)
    print("这是第三个")
    c = yield 333
    print(c)
    print("这是最后一个")
    yield 444
 
g = func()
print(g.__next__())                    # 第一个写next, 而不是send, 通过send的定义, 也能了解到, send()只是给上一个yield的位置传值, 这里还没有上一个
print(g.send("韭菜盒子"))         # send()里面必须写值, 不然报错
print(g.send("锅包肉"))
print(g.send("煎饼果子"))
 
结果:
这是第一个
111
韭菜盒子
这是第二个
222
锅包肉
这是第三个
333
煎饼果子
这是最后一个
444
 
如果不使用send()传值, 我们看一下结果
def func():
    print("这是第一个")
    a = yield 111
    print(a)
    print("这是第二个")
    b = yield 222
    print(b)
    print("这是第三个")
    c = yield 333
    print(c)
    print("这是最后一个")
    yield 444
 
g = func()
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
 
结果:
这是第一个
111
None                # a 取不到值
这是第二个
222
None
这是第三个
333
None
这是最后一个
444
 
图解: a = yield 111 程序执行到这里时, 遇见"什么=什么", 那一定先执行等号右边的, 等号右边的是yield, 
我们说过生成器函数在执行时,遇到yield就返回, 遇到yield就返回, 那执行完等号右边的,本次执行结果, 函数返回结果, 那a=就取不到值, 结果就为None.
使用send()可以在结束时, 给上一个yield的位置传一个值, 正好是a=yield的位置, 所以a就有值了.
 
获取内部的元素
def func():
yield 11
yield 22
yield 33
yield 44
yield 55
 
# for循环
for i in func():        # 使用内部的__next__()来实现
    print(i)
 
# list
print(list(func()))      # 使用内部的__next__()来实现
 
 
二.推导式
1.推导式
用一句话来生成一个列表.
基本语法:
    [结果 for循环]
其他:
[结果 for循环 条件]
 
2.列表推导式
 
# 分别使用算法和推导式, 生成一个列表
# 算法
lst = []
for i in range(1,10):
    lst.append(i)
    print(lst)
 
# 列表推导式
lst = [i for i in range(1,10)]
print(lst)
 
# 加个判断
lst = [i for i in range(100) if i % 2 == 1]
print(lst)
 
注: 等式太复杂,或者太长的话, 都不推荐使用,排错不好排
 
3.字典推导式
用法:{k:v for循环 条件}
 
# 用列表里的值和索引组成一个列表
lst = [11, 22, 33, 44]
dic = {i: lst[i] for i in range(len(lst))}
print(dic)
 
# 结果:
{0: 11, 1: 22, 2: 33, 3: 44}
 
4.集合推导式
用法:{k for循环 条件}
 
# 生成一个集合
lst = [1, 1, 2, 2, 3, 3]
se = {el for el in lst}
print(se)
 
# 结果:
{1, 2, 3}        # 注意集合的特性,去重,所以这里,就三个值
 
注:
没有元组推导式, 这和元组的特性有关, 只能看, 不能增删改, 你想想就这个增,一个一个往里面添加, 是不是, 那肯定做不到了.
 
三.生成器表达式
语法格式:(和推导式的几乎一行, 只是把[]换成())
(结果 for循环 条件)
 
1.获取生成器表达式的元素
# __next__()
ge = (i for i in range(1, 5))
print(ge)                        # 结果: <generator object <genexpr> at 0x000001DE2D73FDB0>
 
print(ge.__next__())        # 取值: 1
print(ge.__next__())        # 2
print(ge.__next__())        # 3
print(ge.__next__())        # 4
print(ge.__next__())        # 报错,StopIteration
 
# for循环, 可迭代
ge = (i for i in range(1, 5))
for i in ge:
    print(i)
 
# list
ge = (i for i in range(1, 5))
print(list(ge))
 
# 结果:
[1, 2, 3, 4]
 
2.生成器表达式和列表推导式的区别
  • 列表推导式比较耗内存, 一次性加载.; 生成器表达式几乎不占用内存, 使用的时候才分配和使用内存.
  • 得到的值不一样.列表推导式得到的是一个列表;生成器表达式获取的是一个生成器.
 
lst = [i for i in range(1, 5)]
print(lst)            # 结果: [1, 2, 3, 4]
 
ge = (i for i in range(1, 5))
print(ge)           # 结果: <generator object <genexpr> at 0x0000020B074BFDB0>
 
 
生成器的惰性机制: 生成器只有在访问的时候才取值, 说白了, 你找他要, 他才给你值, 不找他要, 他是不会执行的.
想想鸡蛋和老母鸡, 列表推导式是给你一筐鸡蛋, 生成器表达式是给你一只鸡,要的时候给你下个蛋, 要的时候给你下个蛋, 就是这样.
原文地址:https://www.cnblogs.com/facelessmen/p/9471490.html