生成器

  • 介绍:根据程序员指定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全部生产出来,而是使用一个再生成一个,可以节约大量的内存。
  • 创建方式:
  1. 生成器推导式;
  2. yield关键字

生成器推导式

  • 与列表推导式类似,只不过生成器推导式使用的是小括号()。具体区别可以查看“Python基础学习笔记”中的“列表解析式和生成表达式(列表推导式)”。
  • for循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。
iter1 = [i for i in range(100000)]  # 列表推导式
iter2 = (i for i in range(100000))  # 生成器推导式
# iter1占了824440字节    iter2占了96字节 (单位应该是字节)
print(f"列表推导式生成的数据占用内容空间的大小:{iter1.__sizeof__()}\n"  
      f"生成推导式生成的诗句占用内存空间的大小:{iter2.__sizeof__()}") 
print(len(iter1))  # 长度是100000
print(iter2)  # 输出的是生成器对象的内存地址
for i in iter2:  # 方式一:for循环可以遍历输出生成器的内容
    print(i)
    if i == 100:
        break

yield关键字(生成器函数)

  • 只要在函数中使用了yield关键字的函数就被称为生成器(generator)。
  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器:调用一个生成器函数,返回的是一个迭代器对象。
  • 代码说明:
  • 在调用生成器运行的过程中,每次遇到yield时函数会
  1. 暂停
  2. 保存当前所有的运行信息
  3. 返回yield的值,并在下一次执行next()方法时
  4. 从当前位置继续运行。
  • 生成器如果把输出成完成,再次获取生成器中的下一个数据会抛出一个StopIteration异常,表示停止迭代异常。
  • while循环内没有处理异常操作,需要手动添加处理异常操作
def iter_yield():
    print("-" * 20)
    yield 1
    print("-" * 20)
    yield 2
    print("-" * 20)
    yield 3

i_y = iter_yield()
# 想要运行生成器函数中代码需要 for 或者 next 调用
# 值 = next(生成器对象):执行一次生成器对象取出一个值,类似于for循环的底层
while True:
    try:
        i = next(i_y)
        print(i)
    except StopIteration as err:
        break
        # sys.exit()

生成器应用场景

  • 通常用yield代替return:当函数需要连续弹出多个值,并且每次只弹出一个值的时候使用,因为return会直接终止程序,而yield不会。
  • 注意yield返回的值被用一次后指针被移到了最后面。
  • 需求
  • 数学中有个著名的费波拉契数列Gibonacci),数列中第一个数为0,第二个数为1,其后每一个数都是由前两个数相加得到:0 1 1 2 3 5 8 13 21 34 …
  • 现在我们使用生成器来实现这个费波拉契数列,每次取值都通过算法来生成下一个数据,生成器每次只调用一个数据,可以节省大量的内存。
# 生成器函数 - 斐波那契
def fibonacci(n): 
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
# f 是一个迭代器,由生成器返回生成
f = fibonacci(10) 
print(f)
# print(tuple(f)) # 可以直接转成元组
# print(list(f)) # 可以直接转成元组

for value in f:
    print(value, end='  ')
print(list(f))  // 使用一次后指针被