-
生成器的产生解决了什么问题
-
生成器创建的两种方式
-
生成器怎样去使用
生成器的产生解决了什么问题?
我们知道利用列表生成式可以制造各式各样的列表,但是不难发现,有时候我们只用到的列表里面的几个元素,并且列表是非常占用机器内存的。如果我们可以创建某种算法,在循环的过程中不断推算出后续的元素,这岂不是节约了大量的内存呢,这种边循环边计算的机制成为生成器:generator
生成器创建的两种方式
- 利用类似于列表生成式的方法创建
好的,我们先来看一下列表的写法:
>>> list1 = [i * i for i in range(10)]
>>>
>>> list1
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
>>> type(list1)
<class 'list'>
>>>
再来看一下生成器,是的很简单,只需要把“[]”换成“()”就可以了
>>> g = (i * i for i in range(10))
>>>
>>> g
<generator object <genexpr> at 0x7fc9b086aa20>
>>>
>>> type(g)
<class 'generator'>
>>>
- 在函数中使用yield生成
首先来看一下在函数中使用print打印出数列的情况:
>>> def number(max):
... for i in range(max + 1):
... print(i)
...
>>>
>>>
>>> list1 = number(10)
0
1
2
3
4
5
6
7
8
9
10
>>>
再来看一下如果使用yield在函数中创建生成式,无非是把print替换成yeild就可以了:
>>> def number(max):
... for i in range(max + 1):
... yield(i)
...
>>> g = number(10)
>>> g
<generator object number at 0x7fc9a8ede138>
>>>
>>> type(g)
<class 'generator'>
>>>
生成器怎样去使用
对于生成式可以使用next()函数区调用,比如:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
5
>>> next(g)
6
>>> next(g)
7
>>> next(g)
8
>>> next(g)
9
>>> next(g)
10
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
从上面的例子可以看到,使用next()实在是太麻烦了,一旦遍历到不存在的元素,并且还会报错,那么用什么方式去使用生成器呢?当然是for循环啦~因为for循环中就内置了next()功能,并且遍历下去还不会报错。
>>> for i in g:
... print(i)
...
>>>
怎么回事,啥也没有出现?好吧,这是小编刚刚发现的问题,由于小编脑洞比较大,所以猜测生成器在被next()函数调用的时候,也是存在一个指针的概念的,由于刚刚的next()函数已经把指针拨到了生成器的尾部,所以尽管再使用for循环内置的next函数时候,就无法再显示内容。因为刚才我说过了,for循环不像next()那样会输出报错啊~
好吧,我们再试一下重新去定义一个生成器,再去使用for循环去验证这个结论:
>>> for i in g:
... print(i)
...
>>>
>>> g = (i * i for i in range(10))
>>>
>>> next(g)
0
>>> next(g)
1
>>>
>>> for i in g:
... print(i)
...
4
9
16
25
36
看,小编的结论应验了~
总结一下:生成器在被遍历的时候是“”一次性”的,实际上是生成器被遍历完之后就相当于读写的指针已经指到了生成器最后面,如果想要像列表一样重复调用,需要再重新定义一遍。
网友评论