生成器理解到底如何工作?


91

生成器理解是做什么的?它是如何工作的?我找不到有关它的教程。


1
需要明确的是,这些语言的名称是生成器表达式,而不是生成器理解
ShadowRanger

1
@ShadowRanger在2018年7月Python-dev邮件列表中,有关于“命名理解语法”的讨论,其中出于一致性的目的,暂定但相当一致地同意将它们称为“生成器理解”。
亚伦·霍尔

Answers:


144

您了解列表理解吗?如果是这样,生成器表达式就像列表理解一样,但是它没有找到您感兴趣的所有项目并将它们打包到列表中,而是等待,并逐个生成表达式中的每个项目。

>>> my_list = [1, 3, 5, 9, 2, 6]
>>> filtered_list = [item for item in my_list if item > 3]
>>> print(filtered_list)
[5, 9, 6]
>>> len(filtered_list)
3
>>> # compare to generator expression
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> print(filtered_gen)  # notice it's a generator object
<generator object <genexpr> at 0x7f2ad75f89e0>
>>> len(filtered_gen) # So technically, it has no length
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
>>> # We extract each item out individually. We'll do it manually first.
... 
>>> next(filtered_gen)
5
>>> next(filtered_gen)
9
>>> next(filtered_gen)
6
>>> next(filtered_gen) # Should be all out of items and give an error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> # Yup, the generator is spent. No values for you!
... 
>>> # Let's prove it gives the same results as our list comprehension
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> gen_to_list = list(filtered_gen)
>>> print(gen_to_list)
[5, 9, 6]
>>> filtered_list == gen_to_list
True
>>> 

由于生成器表达式一次只需要产生一项,因此可以节省大量内存。在需要一次获取一项,根据该项进行大量计算然后移至下一项的情况下,生成器表达式最有意义。如果需要多个值,则还可以使用生成器表达式,一次获取几个。如果在程序继续执行之前需要所有值,请改用列表推导。


3
这里有一个问题。我使用next(gen_name)来获得结果,并且该结果在Python 3中有效。是否有任何特定的情况需要使用__next __()?
安基·瓦希莎

2
@AnkitVashistha没有,总是使用next(...),而不是.__next__()在Python 3
托德休厄尔

@ gotgenes @ AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time。您能举一个关于这种用法的例子吗?谢谢。
LittleZero

19

生成器理解是列表理解的惰性版本。

就像列表理解一样,不同之处在于它返回迭代器而不是列表,即具有next()方法的对象将产生下一个元素。

如果您不熟悉列表推导,请参见此处,有关生成器的信息请参见此处


4

列表/生成器理解是一种可用于从现有列表/生成器创建新列表/生成器的构造。

假设您要生成每个数字从1到10的平方的列表。可以在Python中执行此操作:

>>> [x**2 for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

在这里,range(1,11)生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],但是该range函数不是Python 3.0之前的生成器,因此我使用的构造是列表理解。

如果我想创建一个执行相同功能的生成器,则可以这样做:

>>> (x**2 for x in xrange(1,11))
<generator object at 0x7f0a79273488>

但是,在Python 3中,它range是一个生成器,因此结果仅取决于您使用的语法(方括号或圆括号)。


4
错了 外部表达式是否是生成器与内部表达式是否无关。尽管显然,生成器表达式从列表中获取元素通常没有多大意义,但是您可以做到。
锑2013年

可以更清楚地重写吗?我明白您的意思,但是正如锑所说,您似乎在说其他话。(似乎您在说的话是错误的)
Lyndon White

3

生成器理解是一种创建具有特定结构的生成器的简便方法。假设您想要一个generator将所有偶数一一输出的ayour_list。如果使用函数样式创建它,将是这样的:

def allEvens( L ):
    for number in L:
        if number % 2 is 0:
            yield number

evens = allEvens( yourList )

使用此生成器理解表达式,您可以实现相同的结果:

evens = ( number for number in your_list if number % 2 == 0 )

在这两种情况下,当您通话时next(evens),都会在中获得下一个偶数your_list


0

生成器理解是一种创建可迭代对象的方法,类似于在资源上移动的光标。如果您知道mysql游标或mongodb游标,则可能会知道整个实际数据永远不会一次加载到内存中,而是一次加载一次。光标前后移动,但是内存中始终只有一个行/列表元素。

简而言之,通过使用生成器理解,您可以轻松地在python中创建游标。


-1

生成器理解的另一个示例:

print 'Generator comprehensions'

def sq_num(n):
    for num in (x**2 for x in range(n)):    
        yield num

for x in sq_num(10):
    print x 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.