Python介绍

示例

生成器表达式类似于列表,字典和集合推导,但用括号括起来。当括号用作函数调用的唯一参数时,括号不必存在。

expression = (x**2 for x in range(10))

本示例生成10个第一个完美正方形,包括0(其中x = 0)。

生成器函数与常规函数类似,不同之处在于生成器函数yield的主体中具有一个或多个语句。这些函数不能有return任何值(但是,return如果您想尽早停止生成器,则可以使用empty )。

def function():
    for x in range(10):
        yield x**2

该生成器函数等效于先前的生成器表达式,但输出相同。

注意:所有生成器表达式都具有自己的等效函数,反之亦然。


如果两个括号都可以重复,则可以在不带括号的情况下使用生成器表达式:

sum(i for i in range(10) if i % 2 == 0)   #输出:20
any(x = 0 for x in foo)                   #输出:取决于foo,是对还是错
type(a > b for a in foo if a % 2 == 1)    #Output: <class 'generator'>

代替:

sum((i for i in range(10) if i % 2 == 0))
any((x = 0 for x in foo))
type((a > b for a in foo if a % 2 == 1))

但不是:

fooFunction(i for i in range(10) if i % 2 == 0,foo,bar)
return x = 0 for x in foo
barFunction(baz, a > b for a in foo if a % 2 == 1)


调用generator函数将生成一个generator对象,以后可以对其进行迭代。与其他类型的迭代器不同,生成器对象只能被遍历一次。

g1 = function()
print(g1)  # Out: <generator object function at 0x1012e1888>

请注意,生成器的主体不会立即执行:当您function()在上面的示例中调用时,它会立即返回生成器对象,甚至不执行第一个print语句。与返回列表的函数相比,这允许生成器消耗更少的内存,并且允许创建生成无限长序列的生成器。

因此,生成器经常用于数据科学以及涉及大量数据的其他环境中。另一个优点是,其他代码可以立即使用生成器产生的值,而不必等待完整的序列产生。

但是,如果您需要多次使用生成器生成的值,并且生成它们的成本要比存储高,那么将list生成的值存储为a可能比重新生成序列更好。有关更多详细信息,请参见下面的“重置生成器”。

通常,生成器对象用于循环或需要可迭代的任何函数中:

for x in g1:
    print("Received", x)

# 输出:
# 收到0
# 已收到1
# 收到了4
# 收到9
# 已收到16
# 收到25
# 收到36
# 收到了49
# 收到64
# 收到81

arr1 = list(g1)
# arr1 = [],因为上面的循环已经消耗了所有值。
g2 = function()
arr2 = list(g2)  # arr2 = [0,1,4,9,16,25,36,49,64,81]

由于生成器对象是迭代器,因此可以使用该next()函数手动对其进行迭代。这样做将在每次后续调用中一一返回生成的值。

在幕后,每次调用next()生成器时,Python都会在生成器函数的主体中执行语句,直到命中下一条语句为止yield。此时,它返回yield命令的参数,并记住发生该事件的位置。next()从该点再次调用将恢复执行,并继续直到下yield一条语句。

如果Python在没有遇到更多yields的StopIteration情况下到达了生成器函数的末尾,则会引发一个异常(这是正常的,所有迭代器的行为均相同)。

g3 = function()
a = next(g3)  # a变为0
b = next(g3)  # b变成1
c = next(g3)  # c变成2
...
j = next(g3)  # 引发StopIteration,j保持未定义

请注意,在Python 2中,生成器对象具有可用于手动迭代生成的值的方法。在Python 3中,此方法已被所有迭代器的标准所取代。.next().__next__()

重置发电机

请记住,你只能通过迭代由发电机产生的对象一次。如果您已经遍历脚本中的对象,则进一步尝试将产生None。

如果需要多次使用生成器生成的对象,则可以再次定义生成器函数并再次使用它,或者,可以在首次使用时将生成器函数的输出存储在列表中。如果要处理大量数据,重新定义生成器功能将是一个不错的选择,并且存储所有数据项的列表将占用大量磁盘空间。相反,如果最初生成项目的成本很高,则您可能希望将生成的项目存储在列表中,以便重新使用它们。