Python学习笔记13-生成器与迭代器

生成器

当我们需要产生很大的一个列表时候如果使用列表生成式,会占用比较大的内存空间,这时我们一种能够边循环边计算的机制来不断推断后续的元素,我们成为生成器。

第一种形式

只要把一个列表生成式的[]改成(),就创建了一个生成器:

>>> it = (x*2 for x in [1,2,3])
>>> it
<generator object <genexpr> at 0x10f81e468>

使用next()函数获取生成器的下一个返回值。

>>> next(it)
2
>>> next(it)
4
>>> next(it)
6
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

生成器保存的是算法,每次调用next(g),就计算出it的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

上面这种使用next()是在是太不方便了,我们可以方便的使用for循环:

>>> it = (x*2 for x in [1,2,3])
>>> for x in it:
...     print(x)
...
2
4
6

所以,我们创建了一个生成器 generator 后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration 的错误。

第二种形式

如果一个函数包含yield关键字,该函数就不在是一个普通函数,而变成一个生成器generator

def foo1():
    yield 1
    yield 2
f = foo1()
print(next(f))
print(next(f))

这里,最难理解的就是生成器和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成生成器的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

同样的,把函数改成生成器后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

def foo1():
    yield 1
    yield 2
f = foo1()
for x in f:
    print(x)

但是用for循环调用生成器时,发现拿不到生成器的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:

while True:
    try:
        print(next(o))
    except StopIteration as e:
        print(e.value)
        break

小结

生成器 generator 是非常强大的工具,要理解 generator 的工作原理,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。对于函数改成的 generator 来说,遇到return语句或者执行到函数体最后一行语句,就是结束 generator 的指令,for循环随之结束。

迭代器

我们知道可以直接作用于 for 循环的数据类型有:

  • 集合数据类型,如list、tuple、dict、set、str
  • generator, 如生成器和带yield的函数

这些可以作用于for循环的对象,我们统称为可迭代对象:Iterable

可以使用isinstance()来判断一个对象是否为Iterable对象。

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance((), Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance(set(), Iterable)
True
>>> isinstance('', Iterable)
True
>>> isinstance(123, Iterable)
False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

>>> from collections import Iterator
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance((), Iterator)
False
>>> isinstance('abc', Iterator)
False
>>> isinstance((x for x in [1,2]), Iterator)
True

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

生成迭代器

list、dict、strIterable变成Iterator可以使用iter()函数:

>>> isinstance(iter('abc'), Iterator)
True
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter(()), Iterator)
True
>>> isinstance(iter({}), Iterator)
True
>>> isinstance(iter(set()), Iterator)
True

你可能会问,为什么list、dict、str等数据类型不是Iterator

这是因为 Python 的Iterator对象表示的是一个数据流,Iterator 对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用 list 是永远不可能存储全体自然数的。

小结

  • 凡是可作用于for循环的对象都是Iterable类型;

  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

  • 集合数据类型如list、tuple、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

发布了47 篇原创文章 · 获赞 9 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/u013958997/article/details/81028363