2022年 11月 4日

什么是python的生成器

【前言】

我们可以通过列表生成式直接创建一个列表,但是受内存限制,列表的容量肯定是有限的。而且如果创建一个包含1000万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那么我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的内存空间。在python中这种一边循环一边计算的机制,称为生成器(Generator)。

所以说生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并么有把所有的值都存在内存中,而是在运行时生成值。

【生成器】

在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

生成器是一种数据类型,这种数据类型自动实现迭代器协议(其他数据类型需要调用自己的__iter__方法),所以生成器就是可迭代对象,生成器在python中有两种不同的表达方式:

1. 生成器函数

常规函数的定义,但是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数状态,以便下次从它离开的地方继续执行。

例如使用yield 实现斐波那契数列:

  1. def fibonacci(n): # 生成器函数 - 斐波那契
  2. a, b, counter = 0, 1, 0
  3. while True:
  4. if (counter > n):
  5. return
  6. yield a
  7. a, b = b, a + b
  8. counter += 1
  9. f = fibonacci(10) # f是一个迭代器,由生成器返回生成
  10. while True:
  11. try:
  12. print (next(f), end=" ")
  13. except StopIteration:
  14. sys.exit()
  15. 运行结果:
  16. 0 1 1 2 3 5 8 13 21 34 55

2. 生成器表达式

从形式上看,生成器表达式和列表推导式很像,仅仅是将列表推导式的[]替换为(),但是两者差别很大,生成器表达式可以说组合了迭代和列表解析的功能。

生成器表达式可以理解为一种特殊的生成器函数,类似于lambda表达式和普通函数。和生成器一样,生成器表达式也是返回生成器generator对象,一次只返回一个值。

  1. lt = [x * x for x in range(10)]
  2. lt
  3. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  4. gt = (x * x for x in range(10)) #注意把[]改成()后,不是生成一个tuple,而是生成一个generator
  5. gt
  6. <generator object <genexpr> at 0x00000145904B0C78>
  7. print(gt.__next__())
  8. 0
  9. print(gt.__next__())
  10. 1
  11. print(gt.__next__())
  12. 4
  13. print(gt.__next__())
  14. 9
  15. print(gt.__next__())
  16. 16
  17. print(gt.__next__())
  18. 25
  19. print(gt.__next__())
  20. 36
  21. print(gt.__next__())
  22. 49
  23. print(gt.__next__())
  24. 64
  25. print(gt.__next__())
  26. 81
  27. print(gt.__next__())
  28. Traceback (most recent call last):
  29. File "<stdin>", line 1, in <module>
  30. StopIteration

【总结】

1. 把列表解析的[ ]换成()得到的就是生成器表达式

2. 在 Python 中,使用了 yield 的函数都可被称为生成器(generator)。生成器是一个返回迭代器的函数,只能用于迭代操作。更简单点理解生成器就是一个迭代器。

3. 生成器的好处是可以一边循环一边进行计算,不用一下子就生成一个很大的集合,占用内存空间。生成器的使用节省内存空间。即生成器可以避免不必要的计算,带来性能上的提升;而且会节约空间,可以实现无限循环(无穷大的)的数据结构。