如何、为什么以及何时-您应该使用Python生成器?
自从PEP 255引入以来,生成器一直是Python的重要组成部分。
生成器函数允许您声明一个行为类似于迭代器的函数。
它们允许程序员以快速、简单和干净的方式创建迭代器。
你可能会问,什么是迭代器?
迭代器是一个可以迭代(循环)的对象。它用于抽象数据容器,使其行为类似于可迭代对象。您可能已经每天使用一些可迭代对象:字符串、列表和字典等等。
迭代器由实现迭代器协议的类定义。该协议在类中查找两种方法:__iter__和__next__。
为什么你想要制作迭代器?
节省内存空间
迭代器在实例化时不会计算每个项的值。他们只在你要求时计算它。这被称为惰性评估。
当您有一个非常大的数据集要进行计算时,延迟评估很有用。它允许您在计算整个数据集时立即开始使用数据。
假设我们想要得到所有小于最大值的质数。
我们首先定义检查数字是否为素数的函数:
然后,我们定义将包含__iter__和__next__methods的迭代器类:
Primes以最大值实例化。如果下一个素数大于或等于max,则迭代器将引发StopIteration异常,结束迭代器。
当我们请求迭代器中的下一个元素时,它会将数字增加1并检查它是否为素数。如果不是,它将调用__next__again直到数字为素数。一旦它是,迭代器返回数字。
通过使用迭代器,我们不会在内存中创建素数列表。相反,我们每次请求时都会生成下一个素数。
我们来试试吧:
Primes对象的每次迭代都会调用__next__来生成下一个素数。
迭代器只能迭代一次。如果您尝试再次遍历素数,则不会返回任何值。它将表现得像一个空列表。
现在我们知道迭代器是什么以及如何制作迭代器,我们将继续研究生成器。
生成器
回想一下,生成器函数允许我们以更简单的方式创建迭代器。
生成器将yield语句引入Python。它有点像return,因为它返回一个值。
不同之处在于它保存了函数的状态。下次调用该函数时,执行将从它停止的位置继续执行,其变量值与生成前相同。
如果我们将Primes迭代器转换为生成器,它将如下所示:
现在这是相当pythonic!我们可以做得更好吗?
是!我们可以使用PEP 289引入的Generator Expressions。
这是生成器的列表理解等价物。它的工作方式与列表推导完全相同,但表达式用()而不是[]包围。
以下表达式可以替换上面的生成器函数:
这是Python中生成器的美妙之处。
综上所述...
生成器允许您以非常pythonic的方式创建迭代器。
迭代器允许延迟评估,仅在请求时生成可迭代对象的下一个元素。这对于非常大的数据集很有用。
迭代器和生成器只能迭代一次。
生成器函数优于迭代器。
Generator表达式优于迭代器(仅适用于简单情况)。