生成

生成器 属于 Python

1 个练习

关于 生成器

generator是一种函数或表达式,它返回一种特殊的迭代器,叫做生成器迭代器。 Generator-iterators是惰性的:它们不把values存储在内存中,而是在需要时才_生成_这些值。

生成器函数看起来和其他函数没什么两样,只是其中包含一个或多个yield表达式。 每个yield都会暂停代码执行,并保存当前的执行状态(包括所有局部变量和 try 语句)。 生成器恢复执行时,会从暂停处继续,沿用当时的状态,而普通函数每次调用都会重置。

构造生成器

生成器的构造方式和其他循环或递归函数很像,但它需要一个yield表达式,我们稍后会更深入地探讨它。

举个例子,下面这个函数会返回给定数组中各数字的_平方_。 按现在的写法,必须先处理完所有输入,才能返回任何值:

>>> def squares(list_of_numbers):
...     squares = []
...     for number in list_of_numbers:
...         squares.append(number ** 2)
...     return squares

你可以像这样把它改写成生成器:

>>> def squares_generator(list_of_numbers):
...     for number in list_of_numbers:
...         yield number ** 2

这么做的道理在于:当你不需要_一次性_拿到所有值时,就该用生成器。

这样可以节省内存和运算开销,因为只计算你_当前正在处理_的那个值。

使用生成器

生成器(严格来说是generator-iterator,见下面的提示)是iterator的一种,在 Python 中任何需要iterator或iterable的地方都能使用。 这也包括那些需要iterable或iterator作为实参的_函数_或_对象_。 想深入了解的话,可以看看 How to Make an Iterator in Python。

Note

生成器迭代器是迭代器的一个特殊子集。 所谓Iterators,就是让_可迭代对象_能够被循环遍历的机制/协议。 生成器迭代器与常见 Python iterables返回的迭代器行为非常相似,但有几个重要区别需要注意:

  • 它们是_惰性求值_的;迭代是_单向_的,无法“回退”到之前的值。
  • 遍历返回值会_消耗_它们;既不能重置,也不会保存在内存中。
  • 它们不能排序,也不能反转。
  • 它们不是序列类型,也_没有_indexes。 你无法通过加法或减法引用之前或之后的值,也不能使用方括号([])表示法或切片。
  • 它们没有长度,所以不能与len()函数一起使用。
  • 它们可以是_有限_的,也可以是_无限_的。从_无限_的generator-iterator中收集所有值时要小心!

要使用squares_generator()生成器:

>>> squared_numbers = squares_generator([1, 2, 3, 4])

>>> for square in squared_numbers:
...     print(square)
...
1
4
9
16

生成器中的值也可以通过next()函数来产生或获取。 next()会调用生成器对象的__next__()方法,把生成器代码“推进”或执行到它的yield表达式,该表达式随即“产出”或返回这个值。

>>> squared_numbers = squares_generator([1, 2])

>>> next(squared_numbers)
1
>>> next(squared_numbers)
4

当一个generator被完全耗尽、没有更多值可返回时,它会抛出StopIteration错误。

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

可迭代对象与生成器的区别

生成器是_迭代器_的一个特殊子集。 所谓Iterators,就是让_可迭代对象_能够被循环遍历的机制/协议。 生成器和常见 Python iterables返回的迭代器行为非常相似,但有几个重要区别需要注意:

  • 生成器是_单向_的,无法“回退”到之前的值。

  • 遍历生成器会消耗返回的值,无法重置。

  • 生成器(因为采用惰性求值)不能排序,也不能反转。

  • 生成器_没有_indexes,所以不能通过加法或减法引用之前或之后的值。

  • 生成器不能与len()函数一起使用。

  • 生成器可以是_有限_的,也可以是_无限_的,从_无限_生成器中收集所有值时要小心。

yield 表达式

yield 表达式与return表达式非常相似。

与return表达式_不同_,yield会在_特定的位置_把值交给调用方,并暂停对其余任何值的求值或返回,直到这些值被请求为止。

当yield被求值时,它会暂停所在函数的执行,并返回该函数_在那一刻_的值。

之后函数会_保持在作用域内_,当__next__()被调用时,执行继续进行,直到再次遇到yield为止。

Note

yield表达式不能在函数之外使用。

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1

为什么要用生成器?

生成器在许多场景中都很实用。

处理大型集合时,你可能不想把它的所有值都放进memory。 生成器可以一点一点地处理更大的数据,从而节省内存并提升性能。

当某个过程或计算_复杂_、_昂贵_或_无限_时,生成器也非常有用:

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

这样一来,只要对infinite_sequence对象调用__next__(),它就会返回_前一个数字_加 1。

通过 GitHub 编辑 该链接会在新窗口或标签页中打开

学习 生成器

练习已锁定

再解锁 2 个练习即可练习 生成器