通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含 100 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的 list
,从而节省大量的空间。
在 Python
中,这种一边循环一边计算的机制,称为生成器 generator
。
要创建一个 generator
,有以下几种方法:
1. 通过表达式创建生成器
只要把一个列表生成式的 []
改成 ()
,就创建了一个 generator
。
In [41]: g = (x for x in range(3))
In [42]: type(g)
Out[42]: generator
In [43]: g.next()
Out[43]: 0
In [44]: g.next()
Out[44]: 1
In [45]: g.next()
Out[45]: 2
In [46]: g.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-46-d7e53364a9a7> in <module>()
----> 1 g.next()
StopIteration:
'''
每次调用 next(),就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
'''
因为生成器是可迭代的,所以可以通过循环来获取每一个值。但是你只可以迭代他们一次,不能重复迭代,因为它并没有把所有值存储在内存中,而是实时地生成值:
In [18]: g = (x for x in range(3))
In [19]: for i in g:
...: print i
...:
0
1
2
# 再一次迭代时显示的是空值
In [20]: for i in g:
...: print i
...:
2. 通过函数来创建生成器
如果一个函数定义中包含 yield
关键字,那么这个函数就不再是一个普通函数,而是一个 generator
。generator
和函数的执行流程不一样。
- 函数是顺序执行,遇到
return
语句或者最后一行函数语句就返回; - 而
generator
的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
In [21]: def gen():
...: for i in range(3):
...: yield i
...:
In [22]: g = gen()
In [23]: g.next()
Out[23]: 0
In [24]: g.next()
Out[24]: 1
In [25]: g.next()
Out[25]: 2
In [26]: g.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-26-d7e53364a9a7> in <module>()
----> 1 g.next()
StopIteration:
In [27]: g
Out[27]: <generator object gen at 0x0000000003BA0240>
In [28]: gen
Out[28]: <function __main__.gen>
In [29]: gen()
Out[29]: <generator object gen at 0x0000000003BA01B0>
有关 yield
函数的用法,可以参考 https://blog.youkuaiyun.com/wohu1104/article/details/104533897