【前言】
我们可以通过列表生成式直接创建一个列表,但是受内存限制,列表的容量肯定是有限的。而且如果创建一个包含1000万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那么我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的内存空间。在python中这种一边循环一边计算的机制,称为生成器(Generator)。
所以说生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并么有把所有的值都存在内存中,而是在运行时生成值。
【生成器】
在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
生成器是一种数据类型,这种数据类型自动实现迭代器协议(其他数据类型需要调用自己的__iter__方法),所以生成器就是可迭代对象,生成器在python中有两种不同的表达方式:
1. 生成器函数
常规函数的定义,但是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数状态,以便下次从它离开的地方继续执行。
例如使用yield 实现斐波那契数列:
- 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 = fibonacci(10) # f是一个迭代器,由生成器返回生成
-
- while True:
- try:
- print (next(f), end=" ")
- except StopIteration:
- sys.exit()
-
- 运行结果:
- 0 1 1 2 3 5 8 13 21 34 55
2. 生成器表达式
从形式上看,生成器表达式和列表推导式很像,仅仅是将列表推导式的[]替换为(),但是两者差别很大,生成器表达式可以说组合了迭代和列表解析的功能。
生成器表达式可以理解为一种特殊的生成器函数,类似于lambda表达式和普通函数。和生成器一样,生成器表达式也是返回生成器generator对象,一次只返回一个值。
- lt = [x * x for x in range(10)]
- lt
- [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- gt = (x * x for x in range(10)) #注意把[]改成()后,不是生成一个tuple,而是生成一个generator
- gt
- <generator object <genexpr> at 0x00000145904B0C78>
- print(gt.__next__())
- 0
- print(gt.__next__())
- 1
- print(gt.__next__())
- 4
- print(gt.__next__())
- 9
- print(gt.__next__())
- 16
- print(gt.__next__())
- 25
- print(gt.__next__())
- 36
- print(gt.__next__())
- 49
- print(gt.__next__())
- 64
- print(gt.__next__())
- 81
- print(gt.__next__())
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- StopIteration
【总结】
1. 把列表解析的[ ]换成()得到的就是生成器表达式
2. 在 Python 中,使用了 yield 的函数都可被称为生成器(generator)。生成器是一个返回迭代器的函数,只能用于迭代操作。更简单点理解生成器就是一个迭代器。
3. 生成器的好处是可以一边循环一边进行计算,不用一下子就生成一个很大的集合,占用内存空间。生成器的使用节省内存空间。即生成器可以避免不必要的计算,带来性能上的提升;而且会节约空间,可以实现无限循环(无穷大的)的数据结构。