何为生成器?

接下来,我们更进一步,轻松了解生成器在Python 中的工作原理以及如何定义它们。

定义生成器

正如上一节所述,生成器是Python 中一种特殊类型的函数。该函数不返回单个值,而是返回一个迭代器对象。在生成器函数中,返回值使用yield语句而不是return语句。下面定义了一个简单的生成器函数。代码列表如下:

代码清单片段-01

在上面的清单中,我们定义了一个生成器函数。该函数执行yield语句而不是return关键字。 Yield 语句使该函数成为生成器。当我们调用这个函数时,它将返回(产生)一个迭代器对象。我们再看一下生成器调用:

代码清单片段-02

调用生成器通常类似于创建对象,调用生成器函数并将其分配给变量。

程序运行输出如下:

产生第一个项目

一个

产生第二个项目

产生最后一个项目

C

在应用程序生成器代码中,我们调用firstGenerator()函数,它是一个生成器并返回一个迭代器对象。我们将这个迭代器命名为myIter。然后在此迭代器对象上调用next() 函数。在每次调用next() 时,迭代器都会按各自的顺序执行yield 语句并返回一个项目。

根据规则,这个生成器函数不应包含return 关键字。因为如果这样做,return 语句将终止该函数,并且迭代器的要求将无法得到满足。

现在,让我们借助for 循环定义一个更实用的生成器。在此示例中,我们将定义一个生成器,它将连续生成从0 开始到给定最大限制的数字序列。

代码清单如下:

代码清单片段-03

运行程序的输出类似于以下内容:

0

1

2

3

在上面的清单中,我们定义了一个生成器函数,用于生成从0 到给定数字的整数。正如您所看到的,yield 语句位于for 循环内。请注意,n 的值会自动存储在连续的next() 调用中。

需要注意的一点是,定义生成器时,返回值必须是yield语句。这并不意味着return 语句不能出现在生成器中。只是返回非None 值的return 语句通常放在生成器的末尾,以便为StopIteration 异常添加附加信息,以便调用者可以处理它。示例如下:

Python编程:如何获取生成器和表达式?快来盛盘吧

代码清单片段-04

下面是不进行异常处理的情况下运行程序的输出结果,类似如下:

99

100

回溯(最近一次调用):

文件“.”,第11 行,模块中

打印(下一个(g))

StopIteration: 不支持大于100 的数字生成!

如果对程序进行异常捕获处理(try-except),显示的结果会更加简洁。尝试自己运行一下。

生成器与普通函数

如果函数至少包含一个yield 语句,则该函数是生成器函数。如果需要,您还可以包含其他yield 或return 语句。 Yield 和return 关键字都会从函数中返回一些内容。

return 和yield 关键字之间的区别对于生成器来说非常重要。 return 语句完全终止函数,而yield 语句暂停函数,保存其所有状态,然后在后续调用中恢复执行。

我们调用生成器函数的方式与调用普通函数的方式相同。但在执行过程中,生成器在遇到yield关键字时会暂停。它将迭代器流的当前值发送到调用环境并等待下一次调用。同时,它在内部保存局部变量及其状态。

以下是生成器函数与普通函数不同的关键点:

Generator 函数返回(生成)一个迭代器对象。您无需担心显式创建此迭代器对象,yield 关键字会为您完成这项工作。

Generator 函数必须包含至少一个yield 语句。如果需要,它可以包含多个产量关键字。 Generator函数内部实现了迭代器协议(iter()和next()方法)。 Generator 功能自动保存局部变量及其状态。 Generator 函数在yield 关键字处暂停执行,并将控制权传递给调用者。 当迭代器流不返回值时,Generator 函数会自动引发StopIteration 异常。我们用一个简单的例子来演示普通函数和生成器函数的区别。在此示例中,我们要计算前n 个正整数的总和。为此,我们将定义一个函数,给出前n 个正数的列表。我们将通过两种方式实现这个函数:普通函数和生成器函数。

普通函数代码如下:

代码清单片段-05

运行程序的输出类似于以下内容:

49999995000000

经过的时间(以秒为单位): 1.2067763805389404

在代码清单中,我们定义了一个普通函数,它返回前n 个正整数的列表。当我们调用这个函数时,需要一段时间才能完成执行,因为它创建的列表非常大。它还使用大量内存来完成此任务。

现在让我们为相同的操作定义一个生成器函数。代码清单如下:

代码清单片段-06

程序运行结果类似如下:

49999995000000

(发电机模式)已用时间(以秒为单位): 1.0013225078582764

Python编程:如何获取生成器和表达式?快来盛盘吧

正如您在生成器列表中看到的,生成器在更短的时间内完成相同的任务,并且使用更少的内存资源。因为生成器会一项一项地生成项目,而不是返回完整的列表。

性能提升的主要原因(当我们使用生成器时)是值的惰性生成。这种按需生成值的方法会减少内存的使用。生成器的另一个优点是您不需要等到所有元素都生成后才开始使用它们。

生成器表达式

有时,我们需要简单的生成器来执行代码中相对简单的任务。这就是生成器表达式的用武之地。可以使用生成器表达式轻松地动态创建简单的生成器。

生成器表达式类似于Python 中的lambda 函数。但请记住,lambda 是匿名函数,它允许我们动态创建单行函数。就像lambda 函数一样,生成器表达式创建匿名生成器函数。

生成器表达式的语法看起来像列表理解。不同之处在于我们在生成器表达式中使用括号而不是方括号。请参见示例:

运行结果类似如下:

[1,8,27,64,125]

生成器对象geneexpr位于0x000001337F92BEB0

在上面的清单中,我们借助生成器表达式定义了一个简单的生成器。以下是语法:cubes_gen=(i**3 for i in nums)。您可以在输出中看到生成器对象。众所周知,为了能够获取生成器中的项,我们要么显式调用next() 方法,要么使用for 循环来迭代生成器。接下来,打印cubes_gen对象中的项目:

运行程序,看看遍历的元素项的结果是否与列表推导式相同。

让我们看另一个例子。定义一个将字符串中的字母转换为大写字母的生成器。然后调用next() 方法打印前两个字母。代码示例如下:

运行输出如下:

中号

一个

生成器好处

生成器是很棒的工具,特别是当您需要在相对有限的内存中处理大量数据时。以下是在Python: 中使用生成器的一些主要好处

1)内存效率:

假设您有一个返回非常大的结果序列的简单函数。例如,包含数百万项的列表。您必须等待该函数完成所有执行并将整个列表返回给您。这在时间和内存资源方面显然是低效的。另一方面,如果您使用生成器函数,它将逐个返回项目,您将有机会继续执行下一行代码。而不是等待函数执行列表中的所有项目。因为生成器一次只能为您提供一项。

2)延迟计算:

生成器提供延迟(惰性)评估的能力。惰性求值在实际需要时计算值,而不是在实例化时计算值。假设您有一个大数据集需要计算,惰性计算允许您在整个数据集仍在计算时立即开始使用数据。因为如果您使用生成器,则不需要整个数据集。

3) 易于实现且可读:

生成器非常容易实现并提供良好的代码可读性。请记住,如果您使用生成器,则无需担心__iter__() 和__next__() 方法。您所需要的只是函数中的简单yield 语句。

4)处理无限流:

当您需要表示无限的数据流时,生成器是很好的工具。例如,无限计数器。理论上,您无法在内存中存储无限流,因为您无法确定存储无限流需要多少内存。这就是生成器真正发挥作用的地方,因为它一次只生成一项,它可以表示无限的数据流。它不需要将所有数据流存储在内存中。

用户评论

雨后彩虹

哇,终于找到这篇关于Python生成器的文章了!我一直想知道表达式是怎么玩的,谢谢分享。

    有8位网友表示赞同!

娇眉恨

这个标题太吸引人了,Python编程爱好者必看啊!生成器和表达式一直是我的疑惑,期待学习。

    有7位网友表示赞同!

男神大妈

生成器是Python的一大亮点,这篇文章讲解得挺详细的,不过我更喜欢用列表推导式。

    有10位网友表示赞同!

栀蓝

学Python这么多年,生成器和表达式还是有点模糊,这篇博文真是来对了时候。

    有10位网友表示赞同!

淡抹丶悲伤

盛盘这个词用得太形象了,感觉吃了一顿丰盛的Python大餐。

    有14位网友表示赞同!

景忧丶枫涩帘淞幕雨

之前看别人用生成器,总觉得自己看不懂,这篇博文讲解得挺清晰,感谢分享。

    有6位网友表示赞同!

箜明

生成器和表达式听起来很高级,这篇博文让我对它们有了更深的了解,太棒了!

    有16位网友表示赞同!

?娘子汉

Python编程:如何获取生成器和表达式?快来盛盘吧!哈哈,这个标题太逗了,读完文章感觉收获满满。

    有5位网友表示赞同!

麝香味

虽然生成器和表达式听起来复杂,但作者用通俗易懂的语言讲解,让我这个编程小白也能理解。

    有18位网友表示赞同!

非想

学Python的过程中,生成器和表达式总是让我头疼,这篇博文真是及时雨。

    有18位网友表示赞同!

巴黎盛开的樱花

生成器表达式在处理大量数据时很有用,这篇文章让我对它有了新的认识。

    有10位网友表示赞同!

┲﹊怅惘。

盛盘这个词用得太贴切了,感觉这篇文章就像一份丰盛的Python知识大餐。

    有15位网友表示赞同!

陌上花

Python编程:如何获取生成器和表达式?快来盛盘吧!这标题太有吸引力了,看完文章我确实觉得收获颇丰。

    有7位网友表示赞同!

陌上花

生成器表达式在Python中应用广泛,这篇文章让我对它有了更深入的了解,感谢作者。

    有16位网友表示赞同!

不浪漫罪名

学Python的过程中,生成器和表达式一直是我心中的难题,这篇博文让我豁然开朗。

    有13位网友表示赞同!

绳情

Python编程:如何获取生成器和表达式?快来盛盘吧!这个标题太有创意了,读完文章感觉受益匪浅。

    有7位网友表示赞同!

回到你身边

生成器是Python中的一个高级特性,这篇文章让我对它有了全新的认识。

    有11位网友表示赞同!

空谷幽兰

之前对生成器有点抵触,但看完这篇文章,我觉得它其实并不难理解。

    有12位网友表示赞同!

花菲

Python编程:如何获取生成器和表达式?快来盛盘吧!这个标题太吸引人了,读完文章我对Python编程更有信心了。

    有12位网友表示赞同!

空巷

生成器表达式在处理数据流时非常高效,这篇文章让我对Python编程有了新的认识。

    有16位网友表示赞同!

标签: