生成器是python中非常有用特性,而且十分特殊,特殊到以前学过的语言都没有这种功能,身边的一些学其他语言的开发朋友也都不清楚生成器是什么。
所以在这篇文章中将尽可能详细的解释下生成器是什么,迭代器是什么

可迭代对象

在说生成器之前我们必须得提可迭代对象。可迭代对象是什么,顾名思义迭代器就是支持迭代操作的对象。在python3如列表,字典,字符串都是可迭代对象,简单来说就是任何实现了__next__方法的容器都是可迭代对象,而可迭代对象可以转换成迭代器。

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

看了下面的代码应该会清楚很多

In [7]: a = [1,2,3]

In [8]: b = iter(a)

In [9]: a
Out[9]: [1, 2, 3]

In [10]: b
Out[10]: <list_iterator at 0x10c6fa590>

In [11]: next(b)
Out[11]: 1

In [12]: next(b)
Out[12]: 2

In [13]: next(b)
Out[13]: 3

In [14]: type(a)
Out[14]: list

In [15]: type(b)
Out[15]: list_iterator

通过上面这段代码可以知道可迭代对象实现了__iter__方法,所有实现了__iter__方法的容器都可以转换成迭代器:如list_iterator,字典的话就是dict_keyiterator。迭代器也不是固定的数据类型,不同的容器对应着不同的迭代器。

迭代器

从上面结果中可以得知在python3中任何实现了__iter__和__next__方法的对象都是迭代器。

迭代器的原理有点类似于将容器结合了链表。在执行循环的时候不会把对象一次性全部都加载到内存中,而是要等需要用的时候通过next方法来获取下一个元素,所以for循环实际上就是在不停的调用迭代器的next方法。如果容器中没有更多元素了,迭代器就会抛出异常

In [11]: next(b)
Out[11]: 1

In [12]: next(b)
Out[12]: 2

In [13]: next(b)
Out[13]: 3

In [14]: next(b)
StopIteration   Traceback (most recent call last)

当然我们也可以将它变成无限序列的迭代器

In [27]: from itertools import cycle

In [28]: a = [1,2,3]

In [29]: c = cycle(a)

In [31]: next(c)
Out[31]: 1

In [32]: next(c)
Out[32]: 2

In [33]: next(c)
Out[33]: 3

In [34]: next(c)
Out[34]: 1

来看一下自定义的计数迭代器:

class Count:

    def __init__(self, start=0):
        self.num = start

    def __iter__(self):
        return self

    def __next__(self):
        num = self.num
        self.num += 1
        return num

Count是一个迭代器同样也是个可迭代对象。__iter__决定了它是个可迭代对象,每次调用next()方法会返回num随即num+=1

生成器

生成器(generator)可以理解为一个比较优雅的迭代器,它的写法会比迭代器简洁的多。它不需要实现__iter____next__方法,只需要一个yield关键字即可。

def count(n):
	while True:
		yield n
		n += 1

a = count(0)
print(next(a))
print(next(a))
0
1

或者还有另外一种创建方式,生成器有自己独有的生成器表达式,类似于列表表达式:

In [17]: generator_ex = (x*x for x in range(10))

In [18]: generator_ex
Out[18]: <generator object <genexpr> at 0x10bc232d0>

In [19]: next(generator_ex)
Out[19]: 0

In [23]: sum(generator_ex)
Out[23]: 271

虽然只是一组括号的区别,但是列表表达式返回的是一个列表,而生成器表达式则返回的是generator对象.

生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,此外,相比其它容器对象它更能节省内存和CPU,当然它可以用更少的代码来实现相似的功能。

总结

  • list,dict,str,set,file等都是容易,因为它们都支持for循环所以可以成为可迭代对象
  • 如果这些对象都实现了__iter__方法,那么它们可以被转换成迭代器对象
  • 如果这些对象实现了__next__方法则说明它是一个迭代器,迭代器实行的是懒加载方式,它不会把元素都加载一次性加载到内存中,只有到使用的时候才会去加载
  • 生成器是一种比较优雅的迭代器,可以通过yield来替代return表现。也可以通过生成表达式来体现。

参考文档https://docs.python.org/3/reference/expressions.html#yieldexpr
参考文档https://docs.python.org/3/library/stdtypes.html#iterator-types