Python generator,初学者最容易忽视的神器

Python generator,初学者最容易忽视的神器

generator自PEP 255引入以来,一直是Python非常重要的组成部分。

generator允许我们定义一个函数,使其成为一个迭代器(iterator)。这是一种更快,更简洁的创建迭代器的方法。

那么迭代器又是什么呢?

迭代器是可以在其上进行循环的对象。也就是说,我们可以通过for循环对其进行遍历。与此同时,与可迭代(iterable)对象(string, list, tuple等)不同,迭代器可以通过next()函数获取下一个值。

迭代器由实现了迭代器协议(iterator protocol)的类定义。该协议要求在类中实现两个方法:__iter____next__

既然我们已经有了可迭代对象,为什么还需要创建迭代器呢?原因在于,迭代器可以节省内存空间。

迭代器并不是在初始化的时候就将所有元素的值都计算出来,而是只有在数据被需要时才进行计算。这就是所谓的惰性计算(lazy evaluation)。

惰性计算之所以重要,是因为当我们在操作一个规模较大的数据集合的时候,它允许我们可以立刻对数据进行读取,而不必等到所有数据都计算完成。

比如我们想计算小于某个值的所有质数。

首先,我们定义一个函数来判断一个数是否是质数:

Python generator,初学者最容易忽视的神器

然后我们定义一个迭代器类,并且实现__iter__和__next__方法:

Python generator,初学者最容易忽视的神器

如果下一个质数大于等于max,迭代器将抛出一个StopIteration异常,从而结束迭代。

通过迭代器,我们不需要创建一个list把所有质数保存在内存里。而是仅仅在每次请求下一个质数的时候才生成这个值。

Python generator,初学者最容易忽视的神器

在循环中,每轮迭代Primes对象都会调用__next__方法去产生下一个质数。

迭代器只能被遍历一次。如果你试图再次对其进行遍历,那么将无法得到任何结果。

现在我们知道迭代器是什么了,接下来让我们进入generator的话题。

generator

generator引入了yield指令。yield和return比较类似,它们都会从函数返回一个值。

不同之处在于,yield会保存函数当前的执行状态。当函数再次被调用时,执行将从上次返回之处开始,并保持有变量的值都与yield之前相同。

让我们来把Primes改造成一个generator:

Python generator,初学者最容易忽视的神器

是不是看起来好多了?

更进一步的,利用PEP 289引入的generator表达式(generator expressions),我们可以做得更好。

下列代码可以达到与generator函数相同的效果:

Python generator,初学者最容易忽视的神器

看,这就是Python generator之美!

总结

  • generator允许我们优雅地创建迭代器。
  • generator采用惰性计算,只有在需要时才计算下一个值,非常适合处理大数据集合。
  • 迭代器和generator只能被遍历一次。
  • generator函数可以产生迭代器。
  • generator表达式在特定场景下可以替代generator函数并且实现更为简单。

欢迎大家关注我的头条号并就这一话题进行讨论。


分享到:


相關文章: