参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存的限制,列表容量是非常有限的。并且,创建一个巨大容量(比如一百万)的元素的列表,不仅会占用很大的存储空间,而且我们如果只需要访问其中的前面几个元素的话,那后面绝大多数元素占用的空间则全部浪费了。
因此,如果列表元素可以按照某个算法推算出来,我们便可以在循环的过程中不断地推算出后续的元素。这样我们也就不用创建完整的list,从而节省大量的空间。
在python 中,这种边循环边计算的机制,称为generator,生成器。
创建一个生成器
创建生成器的方法有很多种。
1. 通过()
创建
我们可以把列表生成式的[]
修改为()
,便可以创建一个generator。
>>> g = [g*g for g in range(10) if g%2 == 0]
>>> type(g)
<class 'list'>
>>> a = (g*g for g in range(10) if g%2 == 0)
>>> type(a)
<class 'generator'>
generator 保存的是算法,可以通过next()
的方式调出生成器中的元素。
>>> next(a)
0
>>> next(a)
4
>>> next(a)
16
>>> next(a)
36
>>> next(a)
64
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
但上面这种方式实在太麻烦了。而生成器其实也可以被迭代。所以可以利用for循环。
>>> a = (g*g for g in range(10) if g%2 == 0)
>>> for x in a:
... x
...
0
4
16
36
64
2. 通过yield
创建
如果一个函数定义中包含yield关键字,则这个函数就不再是一个普通函数,而是一个生成器。
值得注意的是,生成器和函数的执行流程不太一样,函数是顺序执行,遇到return 或最后一行就返回。可是如果变成了generator 函数,则会在每次调用next()
时执行,并且遇到yield
返回。再次执行时从上次返回的yield
开始。
普通的函数
def odd():
print('step 1')
return 1 # 会直接在此处跳出返回1
print('step 2')
return 2
print('step 3')
return 3
生成器
def odd():
print('step 1')
yield 1
print('step 2')
yield 3
print('step 3')
yield 5
此时如果调用该生成器
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
遇到yield 则会中断,继续调用则会继续,直到第四次调用时没有语句执行而报错。
迭代器
我们已经知道,可直接作用于for
的数据类型有几种:
1)集合数据类型,如list, tuple, str, dict等。
2)生成器类型,generator。
这些可以直接作用于for 循环的对象称为可迭代对象:Iterable。
- 判断可迭代对象
from collections.abc import Iterable
isinstance([], Iterable) # 判断列表是否为可迭代值
- 判断迭代器
from collections.abc import Iterator
isinstance((x for x in range(10)), Iterator) # 列表生成式组建迭代器
生成器,generator,都是Iterator, 但list, tuple 等不是,它们是Iterable。
- 改变Iterable 为Iterator
可以使用iter()
isinstance(iter([]), Iterator)
迭代器与迭代对象的不同
因为Iterator
本身表示的就只是一个数据流(惰性计算序列),Iterator 可以被next() 调用,并在没有数据时抛出异常StopIteration
。我们可以把这个数据流看作为有序数列,但我们却无法提前得知序列的长度,只能不断通过next() 获得下一个数据。
Iterator甚至可以表示无限大的数据流,如全体自然数,而这是list 完全无法做到的。
网友评论